// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #ifdef CONFIG_MTK_M4U #include #endif #ifdef CONFIG_MTK_IOMMU_V2 #include #include #endif #include #include #include //#ifdef MTK_VPU_SMI_DEBUG_ON //#include //#endif #include "mtk_devinfo.h" #ifndef MTK_VPU_FPGA_PORTING //#include /*#include */ #endif #include #ifdef MTK_PERF_OBSERVER #include #endif #define VPU_TRACE_ENABLED /* #define BYPASS_M4U_DBG */ #include "vpu_hw.h" #include "vpu_reg.h" #include "vpu_cmn.h" #include "vpu_algo.h" #include "vpu_dbg.h" #include "vpu_qos.h" #include "vpu_dump.h" #define ENABLE_PMQOS #ifdef ENABLE_PMQOS #include "helio-dvfsrc-opp.h" #endif #ifdef CONFIG_PM_SLEEP //struct wakeup_source vpu_wake_lock[MTK_VPU_CORE]; struct wakeup_source *vpu_wake_lock[MTK_VPU_CORE]; #endif #include "vpu_dvfs.h" #include "apu_dvfs.h" #include #include "vpu_drv.h" #ifdef ENABLE_EARA_QOS #include "mtk_qos_bound.h" #endif #include //#include #include /* for emi mpu */ #include #include /* opp, mW */ struct VPU_OPP_INFO vpu_power_table[VPU_OPP_NUM] = { {VPU_OPP_0, 1165}, {VPU_OPP_1, 1038}, {VPU_OPP_2, 1008}, {VPU_OPP_3, 988}, {VPU_OPP_4, 932}, {VPU_OPP_5, 658}, {VPU_OPP_6, 564}, {VPU_OPP_7, 521}, {VPU_OPP_8, 456}, {VPU_OPP_9, 315}, {VPU_OPP_10, 275}, {VPU_OPP_11, 210}, {VPU_OPP_12, 138}, }; #include #define CMD_WAIT_TIME_MS (3 * 1000) #define OPP_WAIT_TIME_MS (300) #define PWR_KEEP_TIME_MS (2000) #define OPP_KEEP_TIME_MS (3000) #define SDSP_KEEP_TIME_MS (5000) #define IOMMU_VA_START (0x7DA00000) #define IOMMU_VA_END (0x82600000) #define POWER_ON_MAGIC (2) #define OPPTYPE_VCORE (0) #define OPPTYPE_DSPFREQ (1) #define OPPTYPE_VVPU (2) /* 20180703, 00:00 : vpu log mechanism */ #define HOST_VERSION (0x18070300) /* ion & m4u */ #ifdef CONFIG_MTK_M4U static struct m4u_client_t *m4u_client; #endif static struct ion_client *ion_client; static struct vpu_device *vpu_dev; static wait_queue_head_t cmd_wait; struct vup_service_info { struct task_struct *srvc_task; struct sg_table sg_reset_vector; struct sg_table sg_main_program; struct sg_table sg_algo_binary_data; struct sg_table sg_iram_data; uint64_t vpu_base; uint64_t bin_base; uint64_t iram_data_mva; uint64_t algo_data_mva; vpu_id_t current_algo; vpu_id_t default_algo_num; int thread_var; struct mutex cmd_mutex; bool is_cmd_done; struct mutex state_mutex; enum VpuCoreState state; /* working buffer */ struct vpu_shared_memory *work_buf; unsigned int work_buf_logsize; /* execution kernel library */ struct vpu_shared_memory *exec_kernel_lib; }; static struct vup_service_info vpu_service_cores[MTK_VPU_CORE]; struct vpu_shared_memory *core_shared_data; /* shared data for all cores */ bool exception_isr_check[MTK_VPU_CORE]; #ifndef MTK_VPU_FPGA_PORTING #define VPU_EXCEPTION_MAGIC 0x52310000 static uint32_t vpu_dump_exception; /* clock */ static struct clk *clk_top_dsp_sel; static struct clk *clk_top_dsp1_sel; static struct clk *clk_top_dsp2_sel; //static struct clk *clk_top_dsp3_sel; static struct clk *clk_top_ipu_if_sel; static struct clk *clk_apu_core0_jtag_cg; static struct clk *clk_apu_core0_axi_m_cg; static struct clk *clk_apu_core0_apu_cg; static struct clk *clk_apu_core1_jtag_cg; static struct clk *clk_apu_core1_axi_m_cg; static struct clk *clk_apu_core1_apu_cg; static struct clk *clk_apu_conn_apu_cg; static struct clk *clk_apu_conn_ahb_cg; static struct clk *clk_apu_conn_axi_cg; static struct clk *clk_apu_conn_isp_cg; static struct clk *clk_apu_conn_cam_adl_cg; static struct clk *clk_apu_conn_img_adl_cg; static struct clk *clk_apu_conn_emi_26m_cg; static struct clk *clk_apu_conn_vpu_udi_cg; static struct clk *clk_apu_vcore_ahb_cg; static struct clk *clk_apu_vcore_axi_cg; static struct clk *clk_apu_vcore_adl_cg; static struct clk *clk_apu_vcore_qos_cg; static struct clk *clk_top_clk26m; static struct clk *clk_top_univpll_d3_d8; static struct clk *clk_top_univpll_d3_d4; static struct clk *clk_top_mainpll_d2_d4; static struct clk *clk_top_univpll_d3_d2; static struct clk *clk_top_mainpll_d2_d2; static struct clk *clk_top_univpll_d2_d2; static struct clk *clk_top_mainpll_d3; static struct clk *clk_top_univpll_d3; static struct clk *clk_top_mmpll_d7; static struct clk *clk_top_mmpll_d6; static struct clk *clk_top_adsppll_d5; static struct clk *clk_top_tvdpll_ck; static struct clk *clk_top_univpll_d2; static struct clk *clk_top_adsppll_d4; static struct clk *clk_top_mainpll_d2; static struct clk *clk_top_mmpll_d4; static struct clk *clk_top_apupll_ck; /* mtcmos */ static struct clk *mtcmos_dis; //static struct clk *mtcmos_vpu_vcore_dormant; static struct clk *mtcmos_vpu_vcore_shutdown; //static struct clk *mtcmos_vpu_conn_dormant; static struct clk *mtcmos_vpu_conn_shutdown; //static struct clk *mtcmos_vpu_core0_dormant; static struct clk *mtcmos_vpu_core0_shutdown; //static struct clk *mtcmos_vpu_core1_dormant; static struct clk *mtcmos_vpu_core1_shutdown; //static struct clk *mtcmos_vpu_core2_dormant; /* smi */ static struct clk *clk_mmsys_gals_ipu2mm; static struct clk *clk_mmsys_gals_ipu12mm; static struct clk *clk_mmsys_gals_comm0; static struct clk *clk_mmsys_gals_comm1; static struct clk *clk_mmsys_smi_common; /*direct link*/ //static struct clk *clk_mmsys_ipu_dl_txck; //static struct clk *clk_mmsys_ipu_dl_rx_ck; #endif /* workqueue */ struct my_struct_t { int core; int logbackup; struct delayed_work my_work; }; static struct workqueue_struct *wq; static void vpu_power_counter_routine(struct work_struct *); static struct my_struct_t power_counter_work[MTK_VPU_CORE]; static int vpu_shut_down_ex(int core, int logbackup); /* static struct workqueue_struct *opp_wq; */ static void vpu_opp_keep_routine(struct work_struct *); static void vpu_sdsp_routine(struct work_struct *work); static DECLARE_DELAYED_WORK(opp_keep_work, vpu_opp_keep_routine); static DECLARE_DELAYED_WORK(sdsp_work, vpu_sdsp_routine); /* power */ static struct mutex power_mutex[MTK_VPU_CORE]; static bool is_power_on[MTK_VPU_CORE]; static bool is_power_debug_lock; static bool is_power_disable_off; static struct mutex power_counter_mutex[MTK_VPU_CORE]; static int power_counter[MTK_VPU_CORE]; static struct mutex opp_mutex; static bool force_change_vcore_opp[MTK_VPU_CORE]; static bool force_change_vvpu_opp[MTK_VPU_CORE]; static bool force_change_dsp_freq[MTK_VPU_CORE]; static bool change_freq_first[MTK_VPU_CORE]; static bool opp_keep_flag; static bool sdsp_power_counter; static wait_queue_head_t waitq_change_vcore; static wait_queue_head_t waitq_do_core_executing; static uint8_t max_vcore_opp; static uint8_t max_vvpu_opp; static uint8_t max_dsp_freq; static struct mutex power_lock_mutex; static struct mutex set_power_mutex; /* dvfs */ static struct vpu_dvfs_opps opps; #ifdef ENABLE_PMQOS static struct mtk_pm_qos_request vpu_qos_vvpu_request[MTK_VPU_CORE]; #endif /* jtag */ static bool is_jtag_enabled; /* direct link */ static bool is_locked; static struct mutex lock_mutex; static wait_queue_head_t lock_wait; static struct vpu_lock_power lock_power[VPU_OPP_PRIORIYY_NUM][MTK_VPU_CORE]; static uint8_t max_opp[MTK_VPU_CORE]; static uint8_t min_opp[MTK_VPU_CORE]; static uint8_t max_boost[MTK_VPU_CORE]; static uint8_t min_boost[MTK_VPU_CORE]; static int vpu_init_done; static uint8_t segment_max_opp; static inline int atf_vcore_cg_ctl(int state) { struct arm_smccc_res res; arm_smccc_smc(MTK_SIP_KERNEL_APU_VCORE_CG_CTL , state, 0, 0, 0, 0, 0, 0, &res); return 0; } /*move vcore cg ctl to atf*/ #define vcore_cg_ctl(poweron) \ atf_vcore_cg_ctl(poweron) /* isr handler */ static irqreturn_t vpu0_isr_handler(int irq, void *dev_id); static irqreturn_t vpu1_isr_handler(int irq, void *dev_id); /*regulator id*/ static struct regulator *vvpu_reg_id; /*Check Serivce reject set init vlue*/ typedef irqreturn_t (*ISR_CB)(int, void *); struct ISR_TABLE { ISR_CB isr_fp; unsigned int int_number; char device_name[16]; }; /* Must be the same name with that in device node. */ const struct ISR_TABLE VPU_ISR_CB_TBL[MTK_VPU_CORE] = { {vpu0_isr_handler, 0, "ipu1"}, {vpu1_isr_handler, 0, "ipu2"} }; static inline void lock_command(int core_s, int cmd) { unsigned int core = (unsigned int)core_s; mutex_lock(&(vpu_service_cores[core].cmd_mutex)); vpu_service_cores[core].is_cmd_done = false; vpu_write_field(core, FLD_XTENSA_INFO17, 0); LOG_INF("%s: vpu%d: cmd: %02xh, info00:%xh, info17:%xh\n", __func__, core, cmd, vpu_read_field(core, FLD_XTENSA_INFO00), vpu_read_field(core, FLD_XTENSA_INFO17)); } static void vpu_err_msg(int core, const char *msg) { LOG_ERR("%s: (%d)(%d)(%d)(%d.%d.%d.%d)(%d/%d)(%d/%d/%d)%d\n", msg, core, is_power_debug_lock, opps.vvpu.index, opps.dsp.index, opps.dspcore[0].index, opps.dspcore[1].index, opps.ipu_if.index, max_vvpu_opp, max_dsp_freq, force_change_vvpu_opp[core], force_change_dsp_freq[core], change_freq_first[core], opp_keep_flag); } #define vpu_err_hnd(hw_fail, core, req, key, fmt, args...) \ do { \ pr_info(fmt, ##args); \ vpu_err_msg(core, __func__); \ if (hw_fail) { \ vpu_dmp_create_locked(core, req, fmt, ##args); \ apu_get_power_info(); \ aee_kernel_exception("VPU", \ "\nCRDISPATCH_KEY:" key "\n" fmt, ##args); \ } \ } while (0) static void vpu_status(int core) { } static int wait_idle(int core, uint32_t latency, uint32_t retry) { uint32_t pwait = 0; unsigned int count = 0; do { count++; pwait = vpu_read_field(core, FLD_PWAITMODE); if (pwait) return 0; udelay(latency); } while (count < retry); pr_info("%s: vpu%d: %d us: pwaitmode: %d, info00: 0x%x, info25: 0x%x\n", __func__, core, (latency * retry), pwait, vpu_read_field(core, FLD_XTENSA_INFO00), vpu_read_field(core, FLD_XTENSA_INFO25)); return -ETIMEDOUT; } static inline int wait_command(int core_s) { int ret = 0; int count = 0; bool retry = true; unsigned int core = (unsigned int)core_s; #define CMD_WAIT_STEP_MS 1000 #define CMD_WAIT_COUNT (CMD_WAIT_TIME_MS / CMD_WAIT_STEP_MS) start: ret = wait_event_interruptible_timeout(cmd_wait, vpu_service_cores[core].is_cmd_done, msecs_to_jiffies(CMD_WAIT_STEP_MS)); /* ret == -ERESTARTSYS, if signal interrupt */ if (ret == -ERESTARTSYS) { pr_info("%s: vpu%d: interrupt by signal: ret=%d\n", __func__, core, ret); if (retry) { pr_info("%s: vpu%d: try wait again\n", __func__, core); retry = false; goto start; } goto out; } ++count; if (ret) { /* condition true: cmd done */ ret = 0; } else { /* condition false: timeout or retry*/ if (count >= CMD_WAIT_COUNT) { pr_info("%s: vpu%d: timeout: %d ms\n", __func__, core, CMD_WAIT_TIME_MS); ret = -ETIMEDOUT; } else { vpu_status(core); goto start; } } if (ret < 0) ret = wait_idle(core, 2000 /* ms */, 5 /* times */); out: return ret; } static inline void unlock_command(int core_s) { unsigned int core = (unsigned int)core_s; mutex_unlock(&(vpu_service_cores[core].cmd_mutex)); } static inline int Map_DSP_Freq_Table(int freq_opp) { int freq_value = 0; switch (freq_opp) { case 0: default: freq_value = 750; break; case 1: freq_value = 700; break; case 2: freq_value = 624; break; case 3: freq_value = 594; break; case 4: freq_value = 560; break; case 5: freq_value = 525; break; case 6: freq_value = 450; break; case 7: freq_value = 416; break; case 8: freq_value = 364; break; case 9: freq_value = 312; break; case 10: freq_value = 273; break; case 11: freq_value = 208; break; case 12: freq_value = 137; break; case 13: freq_value = 104; break; case 14: freq_value = 52; break; case 15: freq_value = 26; break; } return freq_value; } /****************************************************************************** * Add MET ftrace event for power profilling. *****************************************************************************/ #if defined(VPU_MET_READY) void MET_Events_DVFS_Trace(void) { int vcore_opp = 0; int dsp_freq = 0, ipu_if_freq = 0, dsp1_freq = 0, dsp2_freq = 0; mutex_lock(&opp_mutex); vcore_opp = opps.vcore.index; dsp_freq = Map_DSP_Freq_Table(opps.dsp.index); ipu_if_freq = Map_DSP_Freq_Table(opps.ipu_if.index); dsp1_freq = Map_DSP_Freq_Table(opps.dspcore[0].index); dsp2_freq = Map_DSP_Freq_Table(opps.dspcore[1].index); mutex_unlock(&opp_mutex); vpu_met_event_dvfs(vcore_opp, dsp_freq, ipu_if_freq, dsp1_freq, dsp2_freq); } void MET_Events_Trace(bool enter, int core_s, int algo_id) { unsigned int core = (unsigned int)core_s; if (enter) { int dsp_freq = 0; mutex_lock(&opp_mutex); dsp_freq = Map_DSP_Freq_Table(opps.dspcore[core].index); mutex_unlock(&opp_mutex); vpu_met_event_enter(core, algo_id, dsp_freq); } else { vpu_met_event_leave(core, algo_id); } } #endif static inline bool vpu_other_core_idle_check(int core) { int i = 0; bool idle = true; LOG_DBG("vpu %s+\n", __func__); for (i = 0 ; i < MTK_VPU_CORE ; i++) { if (i == core) { continue; } else { LOG_DBG("vpu test %d/%d/%d\n", core, i, vpu_service_cores[i].state); mutex_lock(&(vpu_service_cores[i].state_mutex)); switch (vpu_service_cores[i].state) { case VCT_SHUTDOWN: case VCT_BOOTUP: case VCT_IDLE: break; case VCT_EXECUTING: case VCT_NONE: case VCT_VCORE_CHG: idle = false; mutex_unlock( &(vpu_service_cores[i].state_mutex)); goto out; /*break;*/ } mutex_unlock(&(vpu_service_cores[i].state_mutex)); } } LOG_DBG("vpu core_idle_check %d, %d/%d\n", idle, vpu_service_cores[0].state, vpu_service_cores[1].state); out: return idle; } static inline int wait_to_do_change_vcore_opp(int core) { int ret = 0; int retry = 0; /* now this function just return directly. NO WAIT */ if (g_func_mask & VFM_NEED_WAIT_VCORE) { if (g_vpu_log_level > Log_STATE_MACHINE) { LOG_INF("[vpu_%d_0x%x] wait for vcore change now\n", core, g_func_mask); } } else { return ret; } do { ret = wait_event_interruptible_timeout(waitq_change_vcore, vpu_other_core_idle_check(core), msecs_to_jiffies(OPP_WAIT_TIME_MS)); /* ret == -ERESTARTSYS, if signal interrupt */ if (ret < 0) { retry += 1; ret = -EINTR; LOG_ERR("[vpu_%d/%d] %s, %s, ret=%d\n", core, retry, "interrupt by signal", "while wait to change vcore", ret); } else { /* normal case */ if (ret > 0) { ret = 0; } else { ret = -ETIMEDOUT; LOG_ERR("[vpu_%d] %s timeout, ret=%d\n", core, __func__, ret); } break; } } while (retry < 3); return ret; } static inline bool vpu_opp_change_idle_check(int core) { int i = 0; bool idle = true; for (i = 0 ; i < MTK_VPU_CORE ; i++) { if (i == core) { continue; } else { LOG_DBG("vpu test %d/%d/%d\n", core, i, vpu_service_cores[i].state); mutex_lock(&(vpu_service_cores[i].state_mutex)); switch (vpu_service_cores[i].state) { case VCT_SHUTDOWN: case VCT_BOOTUP: case VCT_IDLE: case VCT_EXECUTING: case VCT_NONE: break; case VCT_VCORE_CHG: idle = false; mutex_unlock( &(vpu_service_cores[i].state_mutex)); goto out; /*break;*/ } mutex_unlock(&(vpu_service_cores[i].state_mutex)); } } out: return idle; } static inline int wait_to_do_vpu_running(int core) { int ret = 0; int retry = 0; /* now this function just return directly. NO WAIT */ if (g_func_mask & VFM_NEED_WAIT_VCORE) { if (g_vpu_log_level > Log_STATE_MACHINE) { LOG_INF("[vpu_%d_0x%x] wait for vpu running now\n", core, g_func_mask); } } else { return ret; } do { ret = wait_event_interruptible_timeout(waitq_do_core_executing, vpu_opp_change_idle_check(core), msecs_to_jiffies(OPP_WAIT_TIME_MS)); /* ret == -ERESTARTSYS, if signal interrupt */ if (ret < 0) { retry += 1; ret = -EINTR; LOG_ERR("[vpu_%d/%d] %s, %s, ret=%d\n", core, retry, "interrupt by signal", "while wait to do vpu running", ret); } else { /* normal case */ if (ret > 0) { ret = 0; } else { ret = -ETIMEDOUT; LOG_ERR("[vpu_%d] %s timeout, ret=%d\n", core, __func__, ret); } break; } } while (retry < 3); return ret; } #ifndef MTK_VPU_FPGA_PORTING static int vpu_set_clock_source(struct clk *clk, uint8_t step) { struct clk *clk_src; LOG_DBG("vpu scc(%d)", step); /* set dsp frequency - 0:750 MHz, 1:700 MHz, 2:624 MHz, 3:594 MHz*/ /* set dsp frequency - 4:560 MHz, 5:525 MHz, 6:450 MHz, 7:416 MHz*/ /* set dsp frequency - 8:364 MHz, 9:312 MHz, 10:273 MH, 11:208 MH*/ /* set dsp frequency - 12:137 MHz, 13:104 MHz, 14:52 MHz, 15:26 MHz*/ switch (step) { case 0: clk_src = clk_top_apupll_ck; break; case 1: clk_src = clk_top_adsppll_d4; break; case 2: clk_src = clk_top_univpll_d2; break; case 3: clk_src = clk_top_tvdpll_ck; break; case 4: clk_src = clk_top_adsppll_d5; break; case 5: clk_src = clk_top_mmpll_d6; break; case 6: clk_src = clk_top_mmpll_d7; break; case 7: clk_src = clk_top_univpll_d3; break; case 8: clk_src = clk_top_mainpll_d3; break; case 9: clk_src = clk_top_univpll_d2_d2; break; case 10: clk_src = clk_top_mainpll_d2_d2; break; case 11: clk_src = clk_top_univpll_d3_d2; break; case 12: clk_src = clk_top_mainpll_d2_d4; break; case 13: clk_src = clk_top_univpll_d3_d4; break; case 14: clk_src = clk_top_univpll_d3_d8; break; case 15: clk_src = clk_top_clk26m; break; default: LOG_ERR("wrong freq step(%d)", step); return -EINVAL; } return clk_set_parent(clk, clk_src); } /*set CONN hf_fdsp_ck, VCORE hf_fipu_if_ck*/ static int vpu_if_set_clock_source(struct clk *clk, uint8_t step) { struct clk *clk_src; LOG_DBG("vpu scc(%d)", step); /* set dsp frequency - 0:624 MHz, 1:624 MHz, 2:624 MHz, 3:594 MHz*/ /* set dsp frequency - 4:560 MHz, 5:525 MHz, 6:450 MHz, 7:416 MHz*/ /* set dsp frequency - 8:364 MHz, 9:312 MHz, 10:273 MH, 11:208 MH*/ /* set dsp frequency - 12:137 MHz, 13:104 MHz, 14:52 MHz, 15:26 MHz*/ switch (step) { case 0: clk_src = clk_top_univpll_d2;/*624MHz*/ break; case 1: clk_src = clk_top_univpll_d2;/*624MHz*/ break; case 2: clk_src = clk_top_univpll_d2;/*624MHz*/ break; case 3: clk_src = clk_top_tvdpll_ck; break; case 4: clk_src = clk_top_adsppll_d5; break; case 5: clk_src = clk_top_mmpll_d6; break; case 6: clk_src = clk_top_mmpll_d7; break; case 7: clk_src = clk_top_univpll_d3; break; case 8: clk_src = clk_top_mainpll_d3; break; case 9: clk_src = clk_top_univpll_d2_d2; break; case 10: clk_src = clk_top_mainpll_d2_d2; break; case 11: clk_src = clk_top_univpll_d3_d2; break; case 12: clk_src = clk_top_mainpll_d2_d4; break; case 13: clk_src = clk_top_univpll_d3_d4; break; case 14: clk_src = clk_top_univpll_d3_d8; break; case 15: clk_src = clk_top_clk26m; break; default: LOG_ERR("wrong freq step(%d)", step); return -EINVAL; } return clk_set_parent(clk, clk_src); } #endif int get_vpu_opp(void) { return opps.dsp.index; } EXPORT_SYMBOL(get_vpu_opp); int get_vpu_dspcore_opp(int core_s) { unsigned int core = (unsigned int)core_s; LOG_DBG("[vpu_%d] get opp:%d\n", core, opps.dspcore[core].index); return opps.dspcore[core].index; } EXPORT_SYMBOL(get_vpu_dspcore_opp); int get_vpu_platform_floor_opp(void) { return (VPU_MAX_NUM_OPPS - 1); } EXPORT_SYMBOL(get_vpu_platform_floor_opp); int get_vpu_ceiling_opp(int core_s) { unsigned int core = (unsigned int)core_s; return max_opp[core]; } EXPORT_SYMBOL(get_vpu_ceiling_opp); int get_vpu_opp_to_freq(uint8_t step) { int freq = 0; /* set dsp frequency - 0:750 MHz, 1:700 MHz, 2:624 MHz, 3:594 MHz*/ /* set dsp frequency - 4:560 MHz, 5:525 MHz, 6:450 MHz, 7:416 MHz*/ /* set dsp frequency - 8:364 MHz, 9:312 MHz, 10:273 MH, 11:208 MH*/ /* set dsp frequency - 12:137 MHz, 13:104 MHz, 14:52 MHz, 15:26 MHz*/ switch (step) { case 0: freq = 750; break; case 1: freq = 700; break; case 2: freq = 624; break; case 3: freq = 594; break; case 4: freq = 560; break; case 5: freq = 525; break; case 6: freq = 450; break; case 7: freq = 416; break; case 8: freq = 364; break; case 9: freq = 312; break; case 10: freq = 273; break; case 11: freq = 208; break; case 12: freq = 137; break; case 13: freq = 104; break; case 14: freq = 52; break; case 15: freq = 26; break; default: LOG_ERR("wrong freq step(%d)", step); return -EINVAL; } return freq; } EXPORT_SYMBOL(get_vpu_opp_to_freq); int get_vpu_init_done(void) { return vpu_init_done; } EXPORT_SYMBOL(get_vpu_init_done); static void get_segment_from_efuse(void) { segment_max_opp = 0; LOG_INF("vpu segment_max_opp: %d\n", segment_max_opp); } /* expected range, vvpu_index: 0~15 */ /* expected range, freq_index: 0~15 */ //static void vpu_opp_check(int core, uint8_t vcore_index, uint8_t freq_index) static void vpu_opp_check(int core_s, uint8_t vvpu_index, uint8_t freq_index) { int i = 0; bool freq_check = false; int log_freq = 0, log_max_freq = 0; //int get_vcore_opp = 0; int get_vvpu_opp = 0; unsigned int core = (unsigned int)core_s; if (is_power_debug_lock) { force_change_vcore_opp[core] = false; force_change_vvpu_opp[core] = false; force_change_dsp_freq[core] = false; goto out; } log_freq = Map_DSP_Freq_Table(freq_index); LOG_DBG("opp_check + (%d/%d/%d), ori vvpu(%d)\n", core, vvpu_index, freq_index, opps.vvpu.index); mutex_lock(&opp_mutex); change_freq_first[core] = false; log_max_freq = Map_DSP_Freq_Table(max_dsp_freq); /*segment limitation*/ if (vvpu_index < opps.vvpu.opp_map[segment_max_opp]) vvpu_index = opps.vvpu.opp_map[segment_max_opp]; if (freq_index < segment_max_opp) freq_index = segment_max_opp; /* vvpu opp*/ get_vvpu_opp = vpu_get_hw_vvpu_opp(core); if (vvpu_index < opps.vvpu.opp_map[max_opp[core]]) vvpu_index = opps.vvpu.opp_map[max_opp[core]]; if (vvpu_index > opps.vvpu.opp_map[min_opp[core]]) vvpu_index = opps.vvpu.opp_map[min_opp[core]]; if (freq_index < max_opp[core]) freq_index = max_opp[core]; if (freq_index > min_opp[core]) freq_index = min_opp[core]; LOG_DVFS("opp_check + max_opp%d, min_opp%d,(%d/%d/%d), ori vvpu(%d)", max_opp[core], min_opp[core], core, vvpu_index, freq_index, opps.vvpu.index); #if 0 if ((vcore_index == 0xFF) || (vcore_index == get_vcore_opp)) { LOG_DBG("no need, vcore opp(%d), hw vore opp(%d)\n", vcore_index, get_vcore_opp); force_change_vcore_opp[core] = false; opps.vcore.index = vcore_index; } else { /* opp down, need change freq first*/ if (vcore_index > get_vcore_opp) change_freq_first[core] = true; if (vcore_index < max_vcore_opp) { LOG_INF("vpu bound vcore opp(%d) to %d", vcore_index, max_vcore_opp); vcore_index = max_vcore_opp; } if (vcore_index >= opps.count) { LOG_ERR("wrong vcore opp(%d), max(%d)", vcore_index, opps.count - 1); } else if ((vcore_index < opps.vcore.index) || ((vcore_index > opps.vcore.index) && (!opp_keep_flag))) { opps.vcore.index = vcore_index; force_change_vcore_opp[core] = true; freq_check = true; } } #else //if ((vvpu_index == 0xFF) || (vvpu_index == get_vvpu_opp)) { if (vvpu_index == 0xFF) { LOG_DBG("no need, vvpu opp(%d), hw vore opp(%d)\n", vvpu_index, get_vvpu_opp); force_change_vvpu_opp[core] = false; opps.vvpu.index = vvpu_index; } else { /* opp down, need change freq first*/ if (vvpu_index > get_vvpu_opp) change_freq_first[core] = true; if (vvpu_index < max_vvpu_opp) { LOG_INF("vpu bound vvpu opp(%d) to %d", vvpu_index, max_vvpu_opp); vvpu_index = max_vvpu_opp; } if (vvpu_index >= opps.count) { LOG_ERR("wrong vvpu opp(%d), max(%d)", vvpu_index, opps.count - 1); } else if ((vvpu_index < opps.vvpu.index) || ((vvpu_index > opps.vvpu.index) && (!opp_keep_flag)) || (vvpu_index < get_vvpu_opp) || ((opps.dspcore[core].index < 9) && (get_vvpu_opp >= 3)) || ((opps.dspcore[core].index < 5) && (get_vvpu_opp >= 2)) || ((opps.dspcore[core].index < 1) && (get_vvpu_opp >= 1))) { opps.vvpu.index = vvpu_index; force_change_vvpu_opp[core] = true; freq_check = true; } } #endif /* dsp freq opp */ if (freq_index == 0xFF) { LOG_DBG("no request, freq opp(%d)", freq_index); force_change_dsp_freq[core] = false; } else { if (freq_index < max_dsp_freq) { LOG_INF("vpu bound dsp freq(%dMHz) to %dMHz", log_freq, log_max_freq); freq_index = max_dsp_freq; } if ((opps.dspcore[core].index != freq_index) || (freq_check)) { /* freq_check for all vcore adjust related operation * in acceptable region */ /* vcore not change and dsp change */ //if ((force_change_vcore_opp[core] == false) && if ((force_change_vvpu_opp[core] == false) && (freq_index > opps.dspcore[core].index) && (opp_keep_flag)) { if (g_vpu_log_level > Log_ALGO_OPP_INFO) { LOG_INF("%s(%d) %s (%d/%d_%d/%d)\n", __func__, core, "dsp keep high", force_change_vvpu_opp[core], freq_index, opps.dspcore[core].index, opp_keep_flag); } } else { opps.dspcore[core].index = freq_index; /*To FIX*/ if (opps.vvpu.index == 1 && opps.dspcore[core].index < 1) { /* adjust 0~3 to 4~7 for real table * if needed */ opps.dspcore[core].index = 1; } if (opps.vvpu.index == 2 && opps.dspcore[core].index < 5) { /* adjust 0~3 to 4~7 for real table * if needed */ opps.dspcore[core].index = 5; } if (opps.vvpu.index == 3 && opps.dspcore[core].index < 9) { /* adjust 0~3 to 4~7 for real table * if needed */ opps.dspcore[core].index = 9; } opps.dsp.index = 15; opps.ipu_if.index = 15; for (i = 0 ; i < MTK_VPU_CORE ; i++) { LOG_DBG("%s %s[%d].%s(%d->%d)\n", __func__, "opps.dspcore", core, "index", opps.dspcore[core].index, opps.dsp.index); /* interface should be the max freq of * vpu cores */ if ((opps.dspcore[i].index < opps.dsp.index) && (opps.dspcore[i].index >= max_dsp_freq)) { opps.dsp.index = opps.dspcore[i].index; opps.ipu_if.index = 9; } /*check opps.dsp.index and opps.vvpu.index again*/ if (opps.dsp.index < 9) { if (opps.dsp.index < 1) opps.vvpu.index = 0; else if (opps.dsp.index < 5) opps.vvpu.index = 1; else opps.vvpu.index = 2; } } force_change_dsp_freq[core] = true; opp_keep_flag = true; mod_delayed_work(wq, &opp_keep_work, msecs_to_jiffies(OPP_KEEP_TIME_MS)); } } else { /* vcore not change & dsp not change */ if (g_vpu_log_level > Log_ALGO_OPP_INFO) LOG_INF("opp_check(%d) vcore/dsp no change\n", core); opp_keep_flag = true; mod_delayed_work(wq, &opp_keep_work, msecs_to_jiffies(OPP_KEEP_TIME_MS)); } } mutex_unlock(&opp_mutex); out: LOG_INF("%s(%d)(%d/%d_%d)(%d/%d)(%d.%d.%d.%d)(%d/%d)(%d/%d/%d/%d)%d\n", "opp_check", core, is_power_debug_lock, vvpu_index, freq_index, opps.vvpu.index, get_vvpu_opp, opps.dsp.index, opps.dspcore[0].index, opps.dspcore[1].index, opps.ipu_if.index, max_vvpu_opp, max_dsp_freq, freq_check, force_change_vvpu_opp[core], force_change_dsp_freq[core], change_freq_first[core], opp_keep_flag); } static bool vpu_change_opp(int core_s, int type) { #ifdef MTK_VPU_FPGA_PORTING LOG_INF("[vpu_%d] %d Skip at FPGA", core, type); return true; #else int ret = false; unsigned int core = (unsigned int)core_s; #if 0 if (get_vvpu_DVFS_is_paused_by_ptpod()) { LOG_INF("[vpu_%d] dvfs skip by ptpod", core); return true; } #endif switch (type) { /* vcore opp */ case OPPTYPE_VCORE: LOG_ERR("should not control vcore\n"); break; /* dsp freq opp */ case OPPTYPE_DSPFREQ: mutex_lock(&opp_mutex); LOG_INF("[vpu_%d] %s setclksrc(%d/%d/%d/%d)\n", core, __func__, opps.dsp.index, opps.dspcore[0].index, opps.dspcore[1].index, opps.ipu_if.index); ret = vpu_if_set_clock_source(clk_top_dsp_sel, opps.dsp.index); if (ret) { LOG_ERR("[vpu_%d]fail to set dsp freq, %s=%d, %s=%d\n", core, "step", opps.dsp.index, "ret", ret); goto out; } if (core == 0) { ret = vpu_set_clock_source(clk_top_dsp1_sel, opps.dspcore[core].index); if (ret) { LOG_ERR("[vpu_%d]%s%d freq, %s=%d, %s=%d\n", core, "fail to set dsp_", core, "step", opps.dspcore[core].index, "ret", ret); goto out; } } else if (core == 1) { ret = vpu_set_clock_source(clk_top_dsp2_sel, opps.dspcore[core].index); if (ret) { LOG_ERR("[vpu_%d]%s%d freq, %s=%d, %s=%d\n", core, "fail to set dsp_", core, "step", opps.dspcore[core].index, "ret", ret); goto out; } } ret = vpu_if_set_clock_source(clk_top_ipu_if_sel, opps.ipu_if.index); if (ret) { LOG_ERR("[vpu_%d]%s, %s=%d, %s=%d\n", core, "fail to set ipu_if freq", "step", opps.ipu_if.index, "ret", ret); goto out; } force_change_dsp_freq[core] = false; mutex_unlock(&opp_mutex); #ifdef MTK_PERF_OBSERVER { struct pob_xpufreq_info pxi; pxi.id = core; pxi.opp = opps.dspcore[core].index; pob_xpufreq_update(POB_XPUFREQ_VPU, &pxi); } #endif break; /* vvpu opp */ case OPPTYPE_VVPU: LOG_DBG("[vpu_%d] wait for changing vvpu opp", core); #if 0 ret = wait_to_do_change_vcore_opp(core); if (ret) { LOG_ERR("[vpu_%d] timeout to %s, ret=%d\n", core, "wait_to_do_change_vcore_opp", ret); goto out; } #endif LOG_DBG("[vpu_%d] to do vvpu opp change", core); mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_VCORE_CHG; mutex_unlock(&(vpu_service_cores[core].state_mutex)); mutex_lock(&opp_mutex); vpu_trace_begin("vcore:request"); #ifdef ENABLE_PMQOS switch (opps.vvpu.index) { case 0: mtk_pm_qos_update_request(&vpu_qos_vvpu_request[core], VVPU_OPP_0); break; case 1: mtk_pm_qos_update_request(&vpu_qos_vvpu_request[core], VVPU_OPP_1); break; case 2: mtk_pm_qos_update_request(&vpu_qos_vvpu_request[core], VVPU_OPP_2); break; case 3: default: mtk_pm_qos_update_request(&vpu_qos_vvpu_request[core], VVPU_OPP_3); break; } #else ret = mmdvfs_set_fine_step(MMDVFS_SCEN_VPU_KERNEL, opps.vcore.index); #endif vpu_trace_end(); mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_BOOTUP; mutex_unlock(&(vpu_service_cores[core].state_mutex)); if (ret) { LOG_ERR("[vpu_%d]fail to request vcore, step=%d\n", core, opps.vcore.index); goto out; } LOG_DBG("[vpu_%d] cgopp vvpu=%d\n", core, regulator_get_voltage(vvpu_reg_id)); force_change_vcore_opp[core] = false; force_change_vvpu_opp[core] = false; mutex_unlock(&opp_mutex); wake_up_interruptible(&waitq_do_core_executing); break; default: LOG_DVFS("unexpected type(%d)", type); break; } out: #if defined(VPU_MET_READY) MET_Events_DVFS_Trace(); #endif return true; #endif } int32_t vpu_thermal_en_throttle_cb(uint8_t vcore_opp, uint8_t vpu_opp) { int i = 0; int ret = 0; int vvpu_opp_index = 0; int vpu_freq_index = 0; if (vpu_init_done != 1) return ret; #if 0 bool vpu_down = true; for (i = 0 ; i < MTK_VPU_CORE ; i++) { mutex_lock(&power_counter_mutex[i]); if (power_counter[i] > 0) vpu_down = false; mutex_unlock(&power_counter_mutex[i]); } if (vpu_down) { LOG_INF("[vpu] all vpu are off currently, do nothing\n"); return ret; } #endif #if 0 if ((int)vpu_opp < 4) { vcore_opp_index = 0; vpu_freq_index = vpu_opp; } else { vcore_opp_index = 1; vpu_freq_index = vpu_opp; } #else if (vpu_opp < VPU_MAX_NUM_OPPS) { vvpu_opp_index = opps.vvpu.opp_map[vpu_opp]; vpu_freq_index = opps.dsp.opp_map[vpu_opp]; } else { LOG_ERR("vpu_thermal_en wrong opp(%d)\n", vpu_opp); return -1; } #endif LOG_INF("%s, opp(%d)->(%d/%d)\n", __func__, vpu_opp, vvpu_opp_index, vpu_freq_index); mutex_lock(&opp_mutex); max_dsp_freq = vpu_freq_index; max_vvpu_opp = vvpu_opp_index; mutex_unlock(&opp_mutex); for (i = 0 ; i < MTK_VPU_CORE ; i++) { mutex_lock(&opp_mutex); /* force change for all core under thermal request */ opp_keep_flag = false; mutex_unlock(&opp_mutex); vpu_opp_check(i, vvpu_opp_index, vpu_freq_index); } for (i = 0 ; i < MTK_VPU_CORE ; i++) { if (force_change_dsp_freq[i]) { /* force change freq while running */ switch (vpu_freq_index) { case 0: default: LOG_INF("thermal force bound freq @750MHz\n"); break; case 1: LOG_INF("thermal force bound freq @ 700MHz\n"); break; case 2: LOG_INF("thermal force bound freq @624MHz\n"); break; case 3: LOG_INF("thermal force bound freq @594MHz\n"); break; case 4: LOG_INF("thermal force bound freq @560MHz\n"); break; case 5: LOG_INF("thermal force bound freq @525MHz\n"); break; case 6: LOG_INF("thermal force bound freq @450MHz\n"); break; case 7: LOG_INF("thermal force bound freq @416MHz\n"); break; case 8: LOG_INF("thermal force bound freq @364MHz\n"); break; case 9: LOG_INF("thermal force bound freq @312MHz\n"); break; case 10: LOG_INF("thermal force bound freq @273MHz\n"); break; case 11: LOG_INF("thermal force bound freq @208MHz\n"); break; case 12: LOG_INF("thermal force bound freq @137MHz\n"); break; case 13: LOG_INF("thermal force bound freq @104MHz\n"); break; case 14: LOG_INF("thermal force bound freq @52MHz\n"); break; case 15: LOG_INF("thermal force bound freq @26MHz\n"); break; } /*vpu_change_opp(i, OPPTYPE_DSPFREQ);*/ } if (force_change_vvpu_opp[i]) { /* vcore change should wait */ LOG_INF("thermal force bound vcore opp to %d\n", vvpu_opp_index); /* vcore only need to change one time from * thermal request */ /*if (i == 0)*/ /* vpu_change_opp(i, OPPTYPE_VCORE);*/ } } return ret; } int32_t vpu_thermal_dis_throttle_cb(void) { int ret = 0; if (vpu_init_done != 1) return ret; LOG_INF("%s +\n", __func__); mutex_lock(&opp_mutex); max_vcore_opp = 0; max_dsp_freq = 0; max_vvpu_opp = 0; mutex_unlock(&opp_mutex); LOG_INF("%s -\n", __func__); return ret; } static int vpu_prepare_regulator_and_clock(struct device *pdev) { int ret = 0; /*enable Vvpu*/ /*--Get regulator handle--*/ vvpu_reg_id = regulator_get(pdev, "VMDLA"); if (!vvpu_reg_id) { ret = -ENOENT; LOG_ERR("regulator_get vvpu_reg_id failed\n"); } #ifdef MTK_VPU_FPGA_PORTING LOG_INF("%s skip at FPGA\n", __func__); #else #define PREPARE_VPU_MTCMOS(clk) \ { \ clk = devm_clk_get(pdev, #clk); \ if (IS_ERR(clk)) { \ ret = -ENOENT; \ LOG_ERR("can not find mtcmos: %s\n", #clk); \ } \ } PREPARE_VPU_MTCMOS(mtcmos_dis); PREPARE_VPU_MTCMOS(mtcmos_vpu_vcore_shutdown); PREPARE_VPU_MTCMOS(mtcmos_vpu_conn_shutdown); PREPARE_VPU_MTCMOS(mtcmos_vpu_core0_shutdown); PREPARE_VPU_MTCMOS(mtcmos_vpu_core1_shutdown); #undef PREPARE_VPU_MTCMOS #define PREPARE_VPU_CLK(clk) \ { \ clk = devm_clk_get(pdev, #clk); \ if (IS_ERR(clk)) { \ ret = -ENOENT; \ LOG_ERR("can not find clock: %s\n", #clk); \ } else if (clk_prepare(clk)) { \ ret = -EBADE; \ LOG_ERR("fail to prepare clock: %s\n", #clk); \ } \ } PREPARE_VPU_CLK(clk_mmsys_gals_ipu2mm); PREPARE_VPU_CLK(clk_mmsys_gals_ipu12mm); PREPARE_VPU_CLK(clk_mmsys_gals_comm0); PREPARE_VPU_CLK(clk_mmsys_gals_comm1); PREPARE_VPU_CLK(clk_mmsys_smi_common); PREPARE_VPU_CLK(clk_apu_vcore_ahb_cg); PREPARE_VPU_CLK(clk_apu_vcore_axi_cg); PREPARE_VPU_CLK(clk_apu_vcore_adl_cg); PREPARE_VPU_CLK(clk_apu_vcore_qos_cg); PREPARE_VPU_CLK(clk_apu_conn_apu_cg); PREPARE_VPU_CLK(clk_apu_conn_ahb_cg); PREPARE_VPU_CLK(clk_apu_conn_axi_cg); PREPARE_VPU_CLK(clk_apu_conn_isp_cg); PREPARE_VPU_CLK(clk_apu_conn_cam_adl_cg); PREPARE_VPU_CLK(clk_apu_conn_img_adl_cg); PREPARE_VPU_CLK(clk_apu_conn_emi_26m_cg); PREPARE_VPU_CLK(clk_apu_conn_vpu_udi_cg); PREPARE_VPU_CLK(clk_apu_core0_jtag_cg); PREPARE_VPU_CLK(clk_apu_core0_axi_m_cg); PREPARE_VPU_CLK(clk_apu_core0_apu_cg); PREPARE_VPU_CLK(clk_apu_core1_jtag_cg); PREPARE_VPU_CLK(clk_apu_core1_axi_m_cg); PREPARE_VPU_CLK(clk_apu_core1_apu_cg); PREPARE_VPU_CLK(clk_top_dsp_sel); PREPARE_VPU_CLK(clk_top_dsp1_sel); PREPARE_VPU_CLK(clk_top_dsp2_sel); PREPARE_VPU_CLK(clk_top_ipu_if_sel); PREPARE_VPU_CLK(clk_top_clk26m); PREPARE_VPU_CLK(clk_top_univpll_d3_d8); PREPARE_VPU_CLK(clk_top_univpll_d3_d4); PREPARE_VPU_CLK(clk_top_mainpll_d2_d4); PREPARE_VPU_CLK(clk_top_univpll_d3_d2); PREPARE_VPU_CLK(clk_top_mainpll_d2_d2); PREPARE_VPU_CLK(clk_top_univpll_d2_d2); PREPARE_VPU_CLK(clk_top_mainpll_d3); PREPARE_VPU_CLK(clk_top_univpll_d3); PREPARE_VPU_CLK(clk_top_mmpll_d7); PREPARE_VPU_CLK(clk_top_mmpll_d6); PREPARE_VPU_CLK(clk_top_adsppll_d5); PREPARE_VPU_CLK(clk_top_tvdpll_ck); PREPARE_VPU_CLK(clk_top_univpll_d2); PREPARE_VPU_CLK(clk_top_adsppll_d4); PREPARE_VPU_CLK(clk_top_mainpll_d2); PREPARE_VPU_CLK(clk_top_mmpll_d4); PREPARE_VPU_CLK(clk_top_apupll_ck); #undef PREPARE_VPU_CLK #endif return ret; } static int vpu_enable_regulator_and_clock(int core) { #ifdef MTK_VPU_FPGA_PORTING LOG_INF("%s skip at FPGA\n", __func__); is_power_on[core] = true; force_change_vcore_opp[core] = false; force_change_vvpu_opp[core] = false; force_change_dsp_freq[core] = false; return 0; #else int ret = 0; int ret1 = 0; int get_vcore_opp = 0; int get_vvpu_opp = 0; //bool adjust_vcore = false; bool adjust_vvpu = false; LOG_DBG("[vpu_%d] en_rc + (%d)\n", core, is_power_debug_lock); vpu_trace_begin("%s", __func__); #if 0 get_vcore_opp = vpu_get_hw_vcore_opp(core); if (opps.vcore.index != get_vcore_opp) adjust_vcore = true; #else /*--enable regulator--*/ ret1 = vvpu_regulator_set_mode(true); udelay(100);//slew rate:rising10mV/us if (g_vpu_log_level > Log_STATE_MACHINE) LOG_INF("enable vvpu ret:%d\n", ret1); get_vvpu_opp = vpu_get_hw_vvpu_opp(core); //if (opps.vvpu.index != get_vvpu_opp) adjust_vvpu = true; #endif vpu_trace_begin("vcore:request"); if (adjust_vvpu) { LOG_DBG("[vpu_%d] en_rc wait for changing vcore opp", core); ret = wait_to_do_change_vcore_opp(core); if (ret) { /* skip change vcore in these time */ LOG_WRN("[vpu_%d] timeout to %s(%d/%d), ret=%d\n", core, "wait_to_do_change_vcore_opp", opps.vcore.index, get_vcore_opp, ret); ret = 0; goto clk_on; } LOG_DBG("[vpu_%d] en_rc to do vvpu opp change", core); #ifdef ENABLE_PMQOS switch (opps.vvpu.index) { case 0: mtk_pm_qos_update_request(&vpu_qos_vvpu_request[core], VVPU_OPP_0); break; case 1: mtk_pm_qos_update_request(&vpu_qos_vvpu_request[core], VVPU_OPP_1); break; case 2: mtk_pm_qos_update_request(&vpu_qos_vvpu_request[core], VVPU_OPP_2); break; case 3: default: mtk_pm_qos_update_request(&vpu_qos_vvpu_request[core], VVPU_OPP_3); break; } #else ret = mmdvfs_set_fine_step(MMDVFS_SCEN_VPU_KERNEL, opps.vcore.index); #endif } vpu_trace_end(); if (ret) { LOG_ERR("[vpu_%d]fail to request vcore, step=%d\n", core, opps.vcore.index); goto out; } LOG_DBG("[vpu_%d] adjust(%d,%d) result vvpu=%d\n", core, adjust_vvpu, opps.vvpu.index, regulator_get_voltage(vvpu_reg_id)); LOG_DBG("[vpu_%d] en_rc setmmdvfs(%d) done\n", core, opps.vcore.index); clk_on: #define ENABLE_VPU_MTCMOS(clk) \ { \ if (clk != NULL) { \ if (clk_prepare_enable(clk)) \ LOG_ERR("fail to prepare&enable mtcmos:%s\n", #clk); \ } else { \ LOG_WRN("mtcmos not existed: %s\n", #clk); \ } \ } #define ENABLE_VPU_CLK(clk) \ { \ if (clk != NULL) { \ if (clk_enable(clk)) \ LOG_ERR("fail to enable clock: %s\n", #clk); \ } else { \ LOG_WRN("clk not existed: %s\n", #clk); \ } \ } vpu_trace_begin("clock:enable_source"); ENABLE_VPU_CLK(clk_top_dsp_sel); ENABLE_VPU_CLK(clk_top_ipu_if_sel); ENABLE_VPU_CLK(clk_top_dsp1_sel); ENABLE_VPU_CLK(clk_top_dsp2_sel); vpu_trace_end(); vpu_trace_begin("mtcmos:enable"); ENABLE_VPU_MTCMOS(mtcmos_dis); ENABLE_VPU_MTCMOS(mtcmos_vpu_vcore_shutdown); ENABLE_VPU_MTCMOS(mtcmos_vpu_conn_shutdown); ENABLE_VPU_MTCMOS(mtcmos_vpu_core0_shutdown); ENABLE_VPU_MTCMOS(mtcmos_vpu_core1_shutdown); vpu_trace_end(); udelay(500); vpu_trace_begin("clock:enable"); ENABLE_VPU_CLK(clk_mmsys_gals_ipu2mm); ENABLE_VPU_CLK(clk_mmsys_gals_ipu12mm); ENABLE_VPU_CLK(clk_mmsys_gals_comm0); ENABLE_VPU_CLK(clk_mmsys_gals_comm1); ENABLE_VPU_CLK(clk_mmsys_smi_common); #if 0 /*ENABLE_VPU_CLK(clk_ipu_adl_cabgen);*/ /*ENABLE_VPU_CLK(clk_ipu_conn_dap_rx_cg);*/ /*ENABLE_VPU_CLK(clk_ipu_conn_apb2axi_cg);*/ /*ENABLE_VPU_CLK(clk_ipu_conn_apb2ahb_cg);*/ /*ENABLE_VPU_CLK(clk_ipu_conn_ipu_cab1to2);*/ /*ENABLE_VPU_CLK(clk_ipu_conn_ipu1_cab1to2);*/ /*ENABLE_VPU_CLK(clk_ipu_conn_ipu2_cab1to2);*/ /*ENABLE_VPU_CLK(clk_ipu_conn_cab3to3);*/ /*ENABLE_VPU_CLK(clk_ipu_conn_cab2to1);*/ /*ENABLE_VPU_CLK(clk_ipu_conn_cab3to1_slice);*/ #endif ENABLE_VPU_CLK(clk_apu_vcore_ahb_cg); ENABLE_VPU_CLK(clk_apu_vcore_axi_cg); ENABLE_VPU_CLK(clk_apu_vcore_adl_cg); ENABLE_VPU_CLK(clk_apu_vcore_qos_cg); /*move vcore cg ctl to atf*/ vcore_cg_ctl(1); ENABLE_VPU_CLK(clk_apu_conn_apu_cg); ENABLE_VPU_CLK(clk_apu_conn_ahb_cg); ENABLE_VPU_CLK(clk_apu_conn_axi_cg); ENABLE_VPU_CLK(clk_apu_conn_isp_cg); ENABLE_VPU_CLK(clk_apu_conn_cam_adl_cg); ENABLE_VPU_CLK(clk_apu_conn_img_adl_cg); ENABLE_VPU_CLK(clk_apu_conn_emi_26m_cg); ENABLE_VPU_CLK(clk_apu_conn_vpu_udi_cg); switch (core) { case 0: default: ENABLE_VPU_CLK(clk_apu_core0_jtag_cg); ENABLE_VPU_CLK(clk_apu_core0_axi_m_cg); ENABLE_VPU_CLK(clk_apu_core0_apu_cg); break; case 1: ENABLE_VPU_CLK(clk_apu_core1_jtag_cg); ENABLE_VPU_CLK(clk_apu_core1_axi_m_cg); ENABLE_VPU_CLK(clk_apu_core1_apu_cg); break; } vpu_trace_end(); #undef ENABLE_VPU_MTCMOS #undef ENABLE_VPU_CLK LOG_INF("[vpu_%d] en_rc setclksrc(%d/%d/%d/%d)\n", core, opps.dsp.index, opps.dspcore[0].index, opps.dspcore[1].index, opps.ipu_if.index); ret = vpu_if_set_clock_source(clk_top_dsp_sel, opps.dsp.index); if (ret) { LOG_ERR("[vpu_%d]fail to set dsp freq, step=%d, ret=%d\n", core, opps.dsp.index, ret); goto out; } ret = vpu_set_clock_source(clk_top_dsp1_sel, opps.dspcore[0].index); if (ret) { LOG_ERR("[vpu_%d]fail to set dsp0 freq, step=%d, ret=%d\n", core, opps.dspcore[0].index, ret); goto out; } ret = vpu_set_clock_source(clk_top_dsp2_sel, opps.dspcore[1].index); if (ret) { LOG_ERR("[vpu_%d]fail to set dsp1 freq, step=%d, ret=%d\n", core, opps.dspcore[1].index, ret); goto out; } ret = vpu_if_set_clock_source(clk_top_ipu_if_sel, opps.ipu_if.index); if (ret) { LOG_ERR("[vpu_%d]fail to set ipu_if freq, step=%d, ret=%d\n", core, opps.ipu_if.index, ret); goto out; } out: vpu_trace_end(); if (g_vpu_log_level > Log_STATE_MACHINE) apu_get_power_info(); is_power_on[core] = true; force_change_vcore_opp[core] = false; force_change_vvpu_opp[core] = false; force_change_dsp_freq[core] = false; LOG_DBG("[vpu_%d] en_rc -\n", core); return ret; #endif } void vpu_enable_mtcmos(void) { #define ENABLE_VPU_MTCMOS(clk) \ { \ if (clk != NULL) { \ if (clk_prepare_enable(clk)) \ LOG_ERR("fail to prepare&enable mtcmos:%s\n", #clk); \ } else { \ LOG_WRN("mtcmos not existed: %s\n", #clk); \ } \ } ENABLE_VPU_MTCMOS(mtcmos_dis); ENABLE_VPU_MTCMOS(mtcmos_vpu_vcore_shutdown); ENABLE_VPU_MTCMOS(mtcmos_vpu_conn_shutdown); ENABLE_VPU_MTCMOS(mtcmos_vpu_core0_shutdown); ENABLE_VPU_MTCMOS(mtcmos_vpu_core1_shutdown); udelay(500); /*move vcore cg ctl to atf*/ vcore_cg_ctl(1); #undef ENABLE_VPU_MTCMOS } EXPORT_SYMBOL(vpu_enable_mtcmos); void vpu_disable_mtcmos(void) { #define DISABLE_VPU_MTCMOS(clk) \ { \ if (clk != NULL) { \ clk_disable_unprepare(clk); \ } else { \ LOG_WRN("mtcmos not existed: %s\n", #clk); \ } \ } DISABLE_VPU_MTCMOS(mtcmos_vpu_core1_shutdown); DISABLE_VPU_MTCMOS(mtcmos_vpu_core0_shutdown); DISABLE_VPU_MTCMOS(mtcmos_vpu_conn_shutdown); DISABLE_VPU_MTCMOS(mtcmos_vpu_vcore_shutdown); DISABLE_VPU_MTCMOS(mtcmos_dis); #undef DISABLE_VPU_MTCMOS } EXPORT_SYMBOL(vpu_disable_mtcmos); #ifdef MTK_VPU_SMI_DEBUG_ON static unsigned int vpu_read_smi_bus_debug(int core) { unsigned int smi_bus_value = 0x0; unsigned int smi_bus_vpu_value = 0x0; if ((int)(vpu_dev->smi_cmn_base) != 0) { switch (core) { case 0: default: smi_bus_value = vpu_read_reg32(vpu_dev->smi_cmn_base, 0x414); break; case 1: smi_bus_value = vpu_read_reg32(vpu_dev->smi_cmn_base, 0x418); break; } smi_bus_vpu_value = (smi_bus_value & 0x007FE000) >> 13; } else { LOG_INF("[vpu_%d] null smi_cmn_base\n", core); } LOG_INF("[vpu_%d] read_smi_bus (0x%x/0x%x)\n", core, smi_bus_value, smi_bus_vpu_value); return smi_bus_vpu_value; } #endif static int vpu_disable_regulator_and_clock(int core) { int ret = 0; int ret1 = 0; #ifdef MTK_VPU_FPGA_PORTING LOG_INF("%s skip at FPGA\n", __func__); is_power_on[core] = false; if (!is_power_debug_lock) opps.dspcore[core].index = 7; return ret; #else unsigned int smi_bus_vpu_value = 0x0; /* check there is un-finished transaction in bus before * turning off vpu power */ #ifdef MTK_VPU_SMI_DEBUG_ON smi_bus_vpu_value = vpu_read_smi_bus_debug(core); LOG_INF("[vpu_%d] dis_rc 1 (0x%x)\n", core, smi_bus_vpu_value); if ((int)smi_bus_vpu_value != 0) { mdelay(1); smi_bus_vpu_value = vpu_read_smi_bus_debug(core); LOG_INF("[vpu_%d] dis_rc again (0x%x)\n", core, smi_bus_vpu_value); if ((int)smi_bus_vpu_value != 0) { smi_debug_bus_hanging_detect_ext2(0x1ff, 1, 0, 1); vpu_aee_warn("VPU SMI CHECK", "core_%d fail to check smi, value=%d\n", core, smi_bus_vpu_value); } } #else LOG_DVFS("[vpu_%d] dis_rc + (0x%x)\n", core, smi_bus_vpu_value); #endif #define DISABLE_VPU_CLK(clk) \ { \ if (clk != NULL) { \ clk_disable(clk); \ } else { \ LOG_WRN("clk not existed: %s\n", #clk); \ } \ } switch (core) { case 0: default: DISABLE_VPU_CLK(clk_apu_core0_jtag_cg); DISABLE_VPU_CLK(clk_apu_core0_axi_m_cg); DISABLE_VPU_CLK(clk_apu_core0_apu_cg); break; case 1: DISABLE_VPU_CLK(clk_apu_core1_jtag_cg); DISABLE_VPU_CLK(clk_apu_core1_axi_m_cg); DISABLE_VPU_CLK(clk_apu_core1_apu_cg); break; } #if 0 DISABLE_VPU_CLK(clk_ipu_adl_cabgen); DISABLE_VPU_CLK(clk_ipu_conn_dap_rx_cg); DISABLE_VPU_CLK(clk_ipu_conn_apb2axi_cg); DISABLE_VPU_CLK(clk_ipu_conn_apb2ahb_cg); DISABLE_VPU_CLK(clk_ipu_conn_ipu_cab1to2); DISABLE_VPU_CLK(clk_ipu_conn_ipu1_cab1to2); DISABLE_VPU_CLK(clk_ipu_conn_ipu2_cab1to2); DISABLE_VPU_CLK(clk_ipu_conn_cab3to3); DISABLE_VPU_CLK(clk_ipu_conn_cab2to1); DISABLE_VPU_CLK(clk_ipu_conn_cab3to1_slice); #endif DISABLE_VPU_CLK(clk_apu_conn_apu_cg); DISABLE_VPU_CLK(clk_apu_conn_ahb_cg); DISABLE_VPU_CLK(clk_apu_conn_axi_cg); DISABLE_VPU_CLK(clk_apu_conn_isp_cg); DISABLE_VPU_CLK(clk_apu_conn_cam_adl_cg); DISABLE_VPU_CLK(clk_apu_conn_img_adl_cg); DISABLE_VPU_CLK(clk_apu_conn_emi_26m_cg); DISABLE_VPU_CLK(clk_apu_conn_vpu_udi_cg); DISABLE_VPU_CLK(clk_apu_vcore_ahb_cg); DISABLE_VPU_CLK(clk_apu_vcore_axi_cg); DISABLE_VPU_CLK(clk_apu_vcore_adl_cg); DISABLE_VPU_CLK(clk_apu_vcore_qos_cg); DISABLE_VPU_CLK(clk_mmsys_gals_ipu2mm); DISABLE_VPU_CLK(clk_mmsys_gals_ipu12mm); DISABLE_VPU_CLK(clk_mmsys_gals_comm0); DISABLE_VPU_CLK(clk_mmsys_gals_comm1); DISABLE_VPU_CLK(clk_mmsys_smi_common); LOG_DBG("[vpu_%d] dis_rc flag4\n", core); #define DISABLE_VPU_MTCMOS(clk) \ { \ if (clk != NULL) { \ clk_disable_unprepare(clk); \ } else { \ LOG_WRN("mtcmos not existed: %s\n", #clk); \ } \ } DISABLE_VPU_MTCMOS(mtcmos_vpu_core1_shutdown); DISABLE_VPU_MTCMOS(mtcmos_vpu_core0_shutdown); DISABLE_VPU_MTCMOS(mtcmos_vpu_conn_shutdown); DISABLE_VPU_MTCMOS(mtcmos_vpu_vcore_shutdown); DISABLE_VPU_MTCMOS(mtcmos_dis); DISABLE_VPU_CLK(clk_top_dsp_sel); DISABLE_VPU_CLK(clk_top_ipu_if_sel); DISABLE_VPU_CLK(clk_top_dsp1_sel); DISABLE_VPU_CLK(clk_top_dsp2_sel); #undef DISABLE_VPU_MTCMOS #undef DISABLE_VPU_CLK #ifdef ENABLE_PMQOS LOG_DVFS("[vpu_%d]pc0=%d, pc1=%d\n", core, power_counter[0], power_counter[1]); mtk_pm_qos_update_request(&vpu_qos_vvpu_request[core], VVPU_OPP_3); #else ret = mmdvfs_set_fine_step(MMDVFS_SCEN_VPU_KERNEL, MMDVFS_FINE_STEP_UNREQUEST); #endif if (ret) { LOG_ERR("[vpu_%d]fail to unrequest vcore!\n", core); goto out; } LOG_DBG("[vpu_%d] disable result vvpu=%d\n", core, regulator_get_voltage(vvpu_reg_id)); out: /*--disable regulator--*/ ret1 = vvpu_regulator_set_mode(false); udelay(100);//slew rate:rising10mV/us LOG_DBG("disable vvpu ret:%d\n", ret1); is_power_on[core] = false; if (!is_power_debug_lock) opps.dspcore[core].index = 15; opps.dsp.index = 9; opps.ipu_if.index = 9; if (g_vpu_log_level > Log_STATE_MACHINE) LOG_INF("[vpu_%d] dis_rc -\n", core); return ret; #endif } static void vpu_unprepare_regulator_and_clock(void) { #ifdef MTK_VPU_FPGA_PORTING LOG_INF("%s skip at FPGA\n", __func__); #else #define UNPREPARE_VPU_CLK(clk) \ { \ if (clk != NULL) { \ clk_unprepare(clk); \ clk = NULL; \ } \ } UNPREPARE_VPU_CLK(clk_apu_core0_jtag_cg); UNPREPARE_VPU_CLK(clk_apu_core0_axi_m_cg); UNPREPARE_VPU_CLK(clk_apu_core0_apu_cg); UNPREPARE_VPU_CLK(clk_apu_core1_jtag_cg); UNPREPARE_VPU_CLK(clk_apu_core1_axi_m_cg); UNPREPARE_VPU_CLK(clk_apu_core1_apu_cg); UNPREPARE_VPU_CLK(clk_apu_conn_apu_cg); UNPREPARE_VPU_CLK(clk_apu_conn_ahb_cg); UNPREPARE_VPU_CLK(clk_apu_conn_axi_cg); UNPREPARE_VPU_CLK(clk_apu_conn_isp_cg); UNPREPARE_VPU_CLK(clk_apu_conn_cam_adl_cg); UNPREPARE_VPU_CLK(clk_apu_conn_img_adl_cg); UNPREPARE_VPU_CLK(clk_apu_conn_emi_26m_cg); UNPREPARE_VPU_CLK(clk_apu_conn_vpu_udi_cg); UNPREPARE_VPU_CLK(clk_apu_vcore_ahb_cg); UNPREPARE_VPU_CLK(clk_apu_vcore_axi_cg); UNPREPARE_VPU_CLK(clk_apu_vcore_adl_cg); UNPREPARE_VPU_CLK(clk_apu_vcore_qos_cg); UNPREPARE_VPU_CLK(clk_mmsys_gals_ipu2mm); UNPREPARE_VPU_CLK(clk_mmsys_gals_ipu12mm); UNPREPARE_VPU_CLK(clk_mmsys_gals_comm0); UNPREPARE_VPU_CLK(clk_mmsys_gals_comm1); UNPREPARE_VPU_CLK(clk_mmsys_smi_common); UNPREPARE_VPU_CLK(clk_top_dsp_sel); UNPREPARE_VPU_CLK(clk_top_dsp1_sel); UNPREPARE_VPU_CLK(clk_top_dsp2_sel); UNPREPARE_VPU_CLK(clk_top_ipu_if_sel); UNPREPARE_VPU_CLK(clk_top_clk26m); UNPREPARE_VPU_CLK(clk_top_univpll_d3_d8); UNPREPARE_VPU_CLK(clk_top_univpll_d3_d4); UNPREPARE_VPU_CLK(clk_top_mainpll_d2_d4); UNPREPARE_VPU_CLK(clk_top_univpll_d3_d2); UNPREPARE_VPU_CLK(clk_top_mainpll_d2_d2); UNPREPARE_VPU_CLK(clk_top_univpll_d2_d2); UNPREPARE_VPU_CLK(clk_top_mainpll_d3); UNPREPARE_VPU_CLK(clk_top_univpll_d3); UNPREPARE_VPU_CLK(clk_top_mmpll_d7); UNPREPARE_VPU_CLK(clk_top_mmpll_d6); UNPREPARE_VPU_CLK(clk_top_adsppll_d5); UNPREPARE_VPU_CLK(clk_top_tvdpll_ck); UNPREPARE_VPU_CLK(clk_top_univpll_d2); UNPREPARE_VPU_CLK(clk_top_adsppll_d4); UNPREPARE_VPU_CLK(clk_top_mainpll_d2); UNPREPARE_VPU_CLK(clk_top_mmpll_d4); UNPREPARE_VPU_CLK(clk_top_apupll_ck); #undef UNPREPARE_VPU_CLK #endif } #define MET_VPU_LOG #ifdef MET_VPU_LOG /* log format */ #define VPUBUF_MARKER (0xBEEF) #define VPULOG_START_MARKER (0x55AA) #define VPULOG_END_MARKER (0xAA55) #define MX_LEN_STR_DESC (128) #pragma pack(push) #pragma pack(2) struct vpulog_format_head_t { unsigned short start_mark; unsigned char desc_len; unsigned char action_id; unsigned int sys_timer_h; unsigned int sys_timer_l; unsigned short sessid; }; #pragma pack(pop) struct vpu_log_reader_t { struct list_head list; int core; unsigned int buf_addr; unsigned int buf_size; void *ptr; }; struct my_ftworkQ_struct_t { struct list_head list; spinlock_t my_lock; int pid; struct work_struct my_work; }; static struct my_ftworkQ_struct_t ftrace_dump_work[MTK_VPU_CORE]; static void vpu_dump_ftrace_workqueue(struct work_struct *); static int vpu_check_postcond(int core); static void __MET_PACKET__(int vpu_core_s, unsigned long long wclk, unsigned char action_id, char *str_desc, unsigned int sessid) { char action = 'Z'; char null_str[] = "null"; char *__str_desc = str_desc; int val = 0; unsigned int vpu_core = (unsigned int)vpu_core_s; switch (action_id) { /* For Sync Maker Begin/End */ case 0x01: action = 'B'; break; case 0x02: action = 'E'; break; /* For Counter Maker */ case 0x03: action = 'C'; /* counter type: */ /* bit 0~11: string desc */ /* bit 12~15: count val */ val = *(unsigned int *)(str_desc + 12); break; /* for Async Marker Start/Finish */ case 0x04: action = 'S'; break; case 0x05: action = 'F'; break; } if (str_desc[0] == '\0') { /* null string handle */ __str_desc = null_str; } vpu_met_packet(wclk, action, vpu_core, ftrace_dump_work[vpu_core].pid, sessid + 0x8000 * vpu_core, __str_desc, val); } static void dump_buf(void *ptr, int leng) { int idx = 0; unsigned short *usaddr; for (idx = 0; idx < leng; idx += 16) { usaddr = (unsigned short *)(ptr + idx); vpu_trace_dump("%08x: %04x%04x %04x%04x %04x%04x %04x%04x,", idx, usaddr[0], usaddr[1], usaddr[2], usaddr[3], usaddr[4], usaddr[5], usaddr[6], usaddr[7] ); } } static int vpulog_clone_buffer(int core_s, unsigned int addr, unsigned int size, void *ptr) { int idx = 0; unsigned int core = (unsigned int)core_s; for (idx = 0; idx < size; idx += 4) { /* read 4 bytes from VPU DMEM */ *((unsigned int *)(ptr + idx)) = vpu_read_reg32(vpu_service_cores[core].vpu_base, addr + idx); } /* notify VPU buffer copy is finish */ vpu_write_field(core, FLD_XTENSA_INFO18, 0x00000000); return 0; } static bool vpulog_check_buff_ready(void *buf, int buf_len) { if (VPUBUF_MARKER != *(unsigned short *)buf) { /* front marker is invalid*/ vpu_trace_dump("Error front maker: %04x", *(unsigned short *)buf); return false; } if (VPUBUF_MARKER != *(unsigned short *)(buf + buf_len - 2)) { vpu_trace_dump("Error end maker: %04x", *(unsigned short *)(buf + buf_len - 2)); return false; } return true; } static bool vpulog_check_packet_valid(struct vpulog_format_head_t *lft) { if (lft->start_mark != VPULOG_START_MARKER) { vpu_trace_dump("Error lft->start_mark: %02x", lft->start_mark); return false; } if (lft->desc_len > 0x10) { vpu_trace_dump("Error lft->desc_len: %02x", lft->desc_len); return false; } return true; } static void vpu_log_parser(int vpu_core, void *ptr, int buf_leng) { int idx = 0; void *start_ptr = ptr; int valid_len = 0; struct vpulog_format_head_t *lft; char trace_data[MX_LEN_STR_DESC]; int vpulog_format_header_size = sizeof(struct vpulog_format_head_t); /*check buffer status*/ if (false == vpulog_check_buff_ready(start_ptr, buf_leng)) { vpu_trace_dump("vpulog_check_buff_ready fail: %p %d", ptr, buf_leng); dump_buf(start_ptr, buf_leng); return; } /* get valid length*/ valid_len = *(unsigned short *)(start_ptr + 2); if (valid_len >= buf_leng) { vpu_trace_dump("valid_len: %d large than buf_leng: %d", valid_len, buf_leng); return; } /*data offset start after Marker,Length*/ idx += 4; if (g_vpu_log_level > VpuLogThre_PERFORMANCE) vpu_trace_begin("%s|vpu%d|@%p/%d", __func__, vpu_core, ptr, buf_leng); while (1) { unsigned long long sys_t; int packet_size = 0; void *data_ptr; if (idx >= valid_len) break; lft = (struct vpulog_format_head_t *)(start_ptr + idx); data_ptr = (start_ptr + idx) + vpulog_format_header_size; if (false == vpulog_check_packet_valid(lft)) { vpu_trace_dump("vpulog_check_packet_valid fail"); dump_buf(start_ptr, buf_leng); break; } /*calculate packet size: header + data + end_mark*/ packet_size = vpulog_format_header_size + lft->desc_len + 2; if (idx + packet_size > valid_len) { vpu_trace_dump("error length (idx: %d, packet_size: %d)", idx, packet_size); vpu_trace_dump("out of bound: valid_len: %d", valid_len); dump_buf(start_ptr, buf_leng); break; } if (lft->desc_len > MX_LEN_STR_DESC) { vpu_trace_dump("lft->desc_len(%d) > MX_LEN_STR_DESC(%d)", lft->desc_len, MX_LEN_STR_DESC); dump_buf(start_ptr, buf_leng); break; } memset(trace_data, 0x00, MX_LEN_STR_DESC); if (lft->desc_len > 0) { /*copy data buffer*/ memcpy(trace_data, data_ptr, lft->desc_len); } sys_t = lft->sys_timer_h; sys_t = (sys_t << 32) + (lft->sys_timer_l & 0xFFFFFFFF); __MET_PACKET__( vpu_core, sys_t, lft->action_id, trace_data, lft->sessid ); idx += packet_size; } if (g_vpu_log_level > VpuLogThre_PERFORMANCE) vpu_trace_end(); } static unsigned int addr_tran_vpu2apmcu(int core, unsigned int buf_addr) { unsigned int apmcu_log_buf = 0x0; unsigned int apmcu_log_buf_ofst = 0xFFFFFFFF; switch (core) { case 0: apmcu_log_buf = ((buf_addr & 0x000fffff) | 0x19100000); apmcu_log_buf_ofst = apmcu_log_buf - 0x19100000; break; case 1: apmcu_log_buf = ((buf_addr & 0x000fffff) | 0x19200000); apmcu_log_buf_ofst = apmcu_log_buf - 0x19200000; break; default: LOG_ERR("wrong core(%d)\n", core); goto out; } out: return apmcu_log_buf_ofst; } static void vpu_dump_ftrace_workqueue(struct work_struct *work) { unsigned int log_buf_size = 0x0; struct my_ftworkQ_struct_t *my_work_core = container_of(work, struct my_ftworkQ_struct_t, my_work); struct list_head *entry; struct vpu_log_reader_t *vlr; void *ptr; int vpu_core; if (g_vpu_log_level > VpuLogThre_PERFORMANCE) vpu_trace_begin("VPU_LOG_ISR_BOTTOM_HALF"); /* protect area start */ list_rescan: spin_lock_irq(&(my_work_core->my_lock)); list_for_each(entry, &(my_work_core->list)) { vlr = list_entry(entry, struct vpu_log_reader_t, list); if (vlr != NULL) { log_buf_size = vlr->buf_size; vpu_core = vlr->core; vpu_trace_dump("%s %d addr/size/ptr: %08x/%08x/%p", __func__, __LINE__, vlr->buf_addr, vlr->buf_size, vlr->ptr); ptr = vlr->ptr; if (ptr) { vpu_log_parser(vpu_core, ptr, log_buf_size); kfree(ptr); vlr->ptr = NULL; } else { vpu_trace_dump("my_work_core->ptr is NULL"); } list_del(entry); kfree(vlr); } else { vpu_trace_dump("%s %d vlr is null", __func__, __LINE__); list_del(entry); } spin_unlock_irq(&(my_work_core->my_lock)); goto list_rescan; } spin_unlock_irq(&(my_work_core->my_lock)); /* protect area end */ if (g_vpu_log_level > VpuLogThre_PERFORMANCE) vpu_trace_end(); } #define VPU_MOVE_WAKE_TO_BACK static int isr_common_handler(int core_s) { int req_cmd = 0, normal_check_done = 0; int req_dump = 0; unsigned int apmcu_log_buf_ofst; unsigned int log_buf_addr = 0x0; unsigned int log_buf_size = 0x0; struct vpu_log_reader_t *vpu_log_reader; void *ptr; uint32_t status; unsigned int core = (unsigned int)core_s; LOG_DBG("vpu %d received a interrupt\n", core); /* INFO 17 was used to reply command done */ req_cmd = vpu_read_field(core, FLD_XTENSA_INFO17); LOG_DBG("INFO17=0x%08x\n", req_cmd); vpu_trace_dump("VPU%d_ISR_RECV|INFO17=0x%08x", core, req_cmd); switch (req_cmd) { case 0: break; case VPU_REQ_DO_CHECK_STATE: default: if (vpu_check_postcond(core) == -EBUSY) { /* host may receive isr to dump */ /* ftrace log while d2d is stilling running */ /* but the info17 is set as 0x100 */ /* in this case, we do nothing for */ /* cmd state control */ /* flow while device status is busy */ vpu_trace_dump("VPU%d VPU_REQ_DO_CHECK_STATE BUSY", core); } else { /* other normal cases for cmd state control flow */ normal_check_done = 1; #ifndef VPU_MOVE_WAKE_TO_BACK vpu_service_cores[core].is_cmd_done = true; wake_up_interruptible(&cmd_wait); vpu_trace_dump("VPU%d VPU_REQ_DO_CHECK_STATE OK", core); #endif } break; } /* INFO18 was used to dump MET Log */ req_dump = vpu_read_field(core, FLD_XTENSA_INFO18); LOG_DBG("INFO18=0x%08x\n", req_dump); vpu_trace_dump("VPU%d_ISR_RECV|INFO18=0x%08x", core, req_dump); /* dispatch interrupt by INFO18 */ switch (req_dump) { case 0: break; case VPU_REQ_DO_DUMP_LOG: /* handle output log */ if (g_func_mask & VFM_ROUTINE_PRT_SYSLOG) { log_buf_addr = vpu_read_field(core, FLD_XTENSA_INFO05); log_buf_size = vpu_read_field(core, FLD_XTENSA_INFO06); /* translate vpu address to apmcu */ apmcu_log_buf_ofst = addr_tran_vpu2apmcu(core, log_buf_addr); if (g_vpu_log_level > VpuLogThre_PERFORMANCE) { vpu_trace_dump("[vpu_%d] log buf/size(0x%08x/0x%08x)-> log_apmcu offset(0x%08x)", core, log_buf_addr, log_buf_size, apmcu_log_buf_ofst); } if (apmcu_log_buf_ofst == 0xFFFFFFFF) { LOG_ERR("addr_tran_vpu2apmcu translate fail!\n"); goto info18_out; } /* in ISR we need use ATOMIC flag to alloc memory */ ptr = kmalloc(log_buf_size, GFP_ATOMIC); if (ptr) { if (g_vpu_log_level > VpuLogThre_PERFORMANCE) vpu_trace_begin("VPULOG_ISR_TOPHALF|VPU%d" , core); vpu_log_reader = kmalloc(sizeof(struct vpu_log_reader_t), GFP_ATOMIC); if (vpu_log_reader) { /* fill vpu_log reader's information */ vpu_log_reader->core = core; vpu_log_reader->buf_addr = log_buf_addr; vpu_log_reader->buf_size = log_buf_size; vpu_log_reader->ptr = ptr; LOG_DBG("%s %d [vpu_%d] addr/size/ptr: %08x/%08x/%p\n", __func__, __LINE__, core, log_buf_addr, log_buf_size, ptr); /* clone buffer in isr*/ if (g_vpu_log_level > VpuLogThre_PERFORMANCE) vpu_trace_begin("VPULOG_CLONE_BUFFER|VPU%d|%08x/%u->%p", core, log_buf_addr, log_buf_size, ptr); vpulog_clone_buffer(core, apmcu_log_buf_ofst, log_buf_size, ptr); if (g_vpu_log_level > VpuLogThre_PERFORMANCE) vpu_trace_end(); /* dump_buf(ptr, log_buf_size); */ /* protect area start */ spin_lock(&(ftrace_dump_work[core].my_lock)); list_add_tail(&(vpu_log_reader->list), &(ftrace_dump_work[core].list)); spin_unlock(&(ftrace_dump_work[core].my_lock)); /* protect area end */ /* dump log to ftrace on BottomHalf */ schedule_work(&(ftrace_dump_work[core].my_work)); } else { LOG_ERR("vpu_log_reader alloc fail"); kfree(ptr); } if (g_vpu_log_level > VpuLogThre_PERFORMANCE) vpu_trace_end(); } else { LOG_ERR("vpu buffer alloc fail"); } } break; case VPU_REQ_DO_CLOSED_FILE: break; default: LOG_ERR("vpu %d not support cmd..(%d)\n", core, req_dump); break; } info18_out: /* clear int */ status = vpu_read_field(core, FLD_APMCU_INT); if (status != 0) { LOG_ERR("vpu %d FLD_APMCU_INT = (%d)\n", core, status); } vpu_write_field(core, FLD_APMCU_INT, 1); #ifdef VPU_MOVE_WAKE_TO_BACK if (normal_check_done == 1) { vpu_trace_dump("VPU%d VPU_REQ_DO_CHECK_STATE OK", core); LOG_INF("normal_check_done UNLOCK\n"); vpu_service_cores[core].is_cmd_done = true; wake_up_interruptible(&cmd_wait); } #endif return IRQ_HANDLED; } irqreturn_t vpu0_isr_handler(int irq, void *dev_id) { return isr_common_handler(0); } irqreturn_t vpu1_isr_handler(int irq, void *dev_id) { return isr_common_handler(1); } #else irqreturn_t vpu0_isr_handler(int irq, void *dev_id) { LOG_DBG("vpu 0 received a interrupt\n"); vpu_service_cores[0].is_cmd_done = true; wake_up_interruptible(&cmd_wait); vpu_write_field(0, FLD_APMCU_INT, 1); /* clear int */ return IRQ_HANDLED; } irqreturn_t vpu1_isr_handler(int irq, void *dev_id) { LOG_DBG("vpu 1 received a interrupt\n"); vpu_service_cores[1].is_cmd_done = true; wake_up_interruptible(&cmd_wait); vpu_write_field(1, FLD_APMCU_INT, 1); /* clear int */ return IRQ_HANDLED; } #endif static int pools_are_empty(int core) { return (vpu_pool_is_empty(&vpu_dev->pool[core]) && vpu_pool_is_empty(&vpu_dev->pool_common) && vpu_pool_is_empty(&vpu_dev->pool_multiproc)); } static void vpu_hw_ion_free_handle(struct ion_client *client, struct ion_handle *handle) { LOG_DBG("[vpu] ion_free_handle +\n"); if (!client) { LOG_WRN("[vpu] invalid ion client(0x%p)!\n", client); return; } if (!handle) { LOG_WRN("[vpu] invalid ion handle(0x%p)!\n", handle); return; } if (g_vpu_log_level > Log_STATE_MACHINE) LOG_INF("[vpu] ion_free_handle(0x%p)\n", handle); ion_free(client, handle); } static int vpu_service_routine(void *arg) { struct vpu_user *user = NULL; struct vpu_request *req = NULL; /*struct vpu_algo *algo = NULL;*/ uint8_t vcore_opp_index = 0xFF; uint8_t vvpu_opp_index = 0xFF; uint8_t dsp_freq_index = 0xFF; struct vpu_user *user_in_list = NULL; struct list_head *head = NULL; int *d = (int *)arg; unsigned int service_core = (*d); bool get = false; int i = 0, j = 0, cnt = 0; DEFINE_WAIT_FUNC(wait, woken_wake_function); for (; !kthread_should_stop();) { if (service_core >= MTK_VPU_CORE) { LOG_ERR("invalid core %d\n", service_core); break; } /* wait for requests if there is no one in user's queue */ add_wait_queue(&vpu_dev->req_wait, &wait); while (1) { if (!pools_are_empty(service_core)) break; wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); } remove_wait_queue(&vpu_dev->req_wait, &wait); /* this thread will be stopped if start direct link */ /* todo, no need in this currently */ /*wait_event_interruptible(lock_wait, !is_locked);*/ /* consume the user's queue */ req = NULL; user_in_list = NULL; head = NULL; get = false; cnt = 0; /* 1. multi-process pool */ req = vpu_pool_dequeue(&vpu_dev->pool_multiproc, NULL); /* 2. self pool */ if (!req) { if (i >= VPU_REQ_MAX_NUM_PRIORITY || i < 0) { LOG_ERR("invalid priority %d\n", i); i = 0; } req = vpu_pool_dequeue( &vpu_dev->pool[service_core], &vpu_dev->priority_list[service_core][i]); } /* 3. common pool */ if (!req) req = vpu_pool_dequeue(&vpu_dev->pool_common, NULL); /* suppose that req is null would not happen * due to we check service_pool_is_empty and * common_pool_is_empty */ if (req != NULL) { LOG_DBG("[vpu] service core index...: %d/%d\n", service_core, (*d)); user = (struct vpu_user *)req->user_id; LOG_DBG("[vpu_%d] user...0x%lx/0x%lx/0x%lx/0x%lx\n", service_core, (unsigned long)user, (unsigned long)&user, (unsigned long)req->user_id, (unsigned long)&(req->user_id)); mutex_lock(&vpu_dev->user_mutex); /* check to avoid user had been removed from list, * and kernel vpu thread still do the request */ list_for_each(head, &vpu_dev->user_list) { user_in_list = vlist_node_of(head, struct vpu_user); LOG_DBG("[vpu_%d] user->id = 0x%lx, 0x%lx\n", service_core, (unsigned long)(user_in_list->id), (unsigned long)(user)); if ((unsigned long)(user_in_list->id) == (unsigned long)(user)) { get = true; LOG_DBG("[vpu_%d] get_0x%lx = true\n", service_core, (unsigned long)(user)); break; } } if (!get) { mutex_unlock(&vpu_dev->user_mutex); LOG_WRN("[vpu_%d] %s(0x%lx) %s\n", service_core, "get request that the original user", (unsigned long)(user), "is deleted"); #define ION_FREE_HANDLE \ { \ for (j = 0 ; j < req->buffers[i].plane_count ; j++) { \ vpu_hw_ion_free_handle(my_ion_client, \ (struct ion_handle *)((uintptr_t)(req->buf_ion_infos[cnt]))); \ cnt++; \ } \ } /* release buf ref cnt if user is deleted*/ for (i = 0 ; i < req->buffer_count ; i++) ION_FREE_HANDLE #undef ION_FREE_HANDLE vpu_hw_ion_free_handle(my_ion_client, (struct ion_handle *)((uintptr_t)(req->sett.sett_ion_fd))); continue; } mutex_lock(&user->data_mutex); user->running[service_core] = true; /* */ mutex_unlock(&user->data_mutex); /* unlock for avoiding long time locking */ mutex_unlock(&vpu_dev->user_mutex); #if 1 if (req->power_param.opp_step == 0xFF) { vcore_opp_index = 0xFF; vvpu_opp_index = 0xFF; dsp_freq_index = 0xFF; } else { vcore_opp_index = opps.vcore.opp_map[req->power_param.opp_step]; vvpu_opp_index = opps.vvpu.opp_map[req->power_param.opp_step]; #define TEMP_VAR (opps.dspcore[service_core].opp_map[req->power_param.opp_step]) dsp_freq_index = TEMP_VAR; #undef TEMP_VAR /*if running req priority < priority list, increase opp*/ LOG_DBG("[vpu_%d] req->priority:%d\n", service_core, req->priority); for (i = 0 ; i < req->priority ; i++) { LOG_DBG("[vpu_%d] priority_list num[%d]:%d\n", service_core, i, vpu_dev->priority_list[service_core][i]); if (vpu_dev->priority_list[service_core][i] > 0) { LOG_INF("+ opp due to priority %d\n", req->priority); vcore_opp_index = 0; dsp_freq_index = 0; break; } } } #else vcore_opp_index = req->power_param.opp_step; dsp_freq_index = req->power_param.freq_step; #endif LOG_DBG("[vpu_%d] run, opp(%d/%d/%d)\n", service_core, req->power_param.opp_step, vvpu_opp_index, dsp_freq_index); vpu_opp_check(service_core, vvpu_opp_index, dsp_freq_index); #define LOG_STRING \ "[v%d<-0x%x]0x%lx,ID=0x%lx_%d,%d->%d,%d,%d/%d-%d,%d-%d,0x%x,%d/%d/%d/0x%x\n" LOG_INF(LOG_STRING, service_core, req->requested_core, (unsigned long)req->user_id, (unsigned long)req->request_id, req->frame_magic, vpu_service_cores[service_core].current_algo, (int)(req->algo_id[service_core]), req->power_param.opp_step, req->power_param.freq_step, vvpu_opp_index, opps.vcore.index, dsp_freq_index, opps.dspcore[service_core].index, g_func_mask, vpu_dev->pool[service_core].size, vpu_dev->pool_common.size, is_locked, efuse_data); #undef LOG_STRING #ifdef CONFIG_PM_SLEEP //__pm_stay_awake(&(vpu_wake_lock[service_core])); __pm_stay_awake(vpu_wake_lock[service_core]); #endif exception_isr_check[service_core] = true; if (vpu_hw_processing_request(service_core, req)) { LOG_ERR("[vpu_%d] %s failed @ Q\n", service_core, "hw_processing_request"); req->status = VPU_REQ_STATUS_FAILURE; goto out; } else { req->status = VPU_REQ_STATUS_SUCCESS; } LOG_DBG("[vpu] flag - 5: hw enque_request done\n"); } else { /* consider that only one req in common pool and all * services get pass through. * do nothing if the service do not get the request */ LOG_WRN("[vpu_%d] get null request, %d/%d/%d\n", service_core, vpu_dev->pool[service_core].size, vpu_dev->pool_common.size, is_locked); continue; } out: cnt = 0; for (i = 0 ; i < req->buffer_count ; i++) { for (j = 0 ; j < req->buffers[i].plane_count ; j++) { vpu_hw_ion_free_handle(my_ion_client, (struct ion_handle *) ((uintptr_t)(req->buf_ion_infos[cnt]))); cnt++; } } vpu_hw_ion_free_handle(my_ion_client, (struct ion_handle *) ((uintptr_t)(req->sett.sett_ion_fd))); /* if req is null, we should not do anything of * following codes */ mutex_lock(&(vpu_service_cores[service_core].state_mutex)); if (vpu_service_cores[service_core].state != VCT_SHUTDOWN) vpu_service_cores[service_core].state = VCT_IDLE; mutex_unlock(&(vpu_service_cores[service_core].state_mutex)); #ifdef CONFIG_PM_SLEEP //__pm_relax(&(vpu_wake_lock[service_core])); __pm_relax(vpu_wake_lock[service_core]); #endif mutex_lock(&vpu_dev->user_mutex); LOG_DBG("[vpu] flag - 5.5 : ....\n"); /* check to avoid user had been removed from list, * and kernel vpu thread finish the task */ get = false; head = NULL; user_in_list = NULL; list_for_each(head, &vpu_dev->user_list) { user_in_list = vlist_node_of(head, struct vpu_user); if ((unsigned long)(user_in_list->id) == (unsigned long)(user)) { get = true; break; } } if (get) { mutex_lock(&user->data_mutex); LOG_DBG("[vpu] flag - 6: add to deque list\n"); req->occupied_core = (0x1 << service_core); list_add_tail(vlist_link(req, struct vpu_request), &user->deque_list); LOG_INF("[vpu_%d, 0x%x->0x%x] %s(%d_%d), st(%d) %s\n", service_core, req->requested_core, req->occupied_core, "algo_id", (int)(req->algo_id[service_core]), req->frame_magic, req->status, "add to deque list done"); user->running[service_core] = false; mutex_unlock(&user->data_mutex); wake_up_interruptible_all(&user->deque_wait); wake_up_interruptible_all(&user->delete_wait); } else { LOG_WRN("[vpu_%d]%s(0x%lx) is deleted\n", service_core, "done request that the original user", (unsigned long)(user)); } mutex_unlock(&vpu_dev->user_mutex); wake_up_interruptible(&waitq_change_vcore); /* leave loop of round-robin */ if (is_locked) break; /* release cpu for another operations */ usleep_range(1, 10); } return 0; } #ifndef MTK_VPU_EMULATOR static int vpu_map_mva_of_bin(int core_s, uint64_t bin_pa) { int ret = 0; unsigned int core = (unsigned int)core_s; #ifndef BYPASS_M4U_DBG #ifdef CONFIG_MTK_IOMMU_V2 struct ion_handle *vpu_handle = NULL; struct ion_mm_data vpu_data; unsigned long fix_mva = 0; #endif uint32_t mva_reset_vector; uint32_t mva_main_program; #ifdef CONFIG_MTK_M4U uint32_t mva_algo_binary_data; uint32_t mva_iram_data; struct sg_table *sg; #endif uint64_t binpa_reset_vector; uint64_t binpa_main_program; uint64_t binpa_iram_data; const uint64_t size_algos = VPU_SIZE_BINARY_CODE - VPU_OFFSET_ALGO_AREA - (VPU_SIZE_BINARY_CODE - VPU_OFFSET_MAIN_PROGRAM_IMEM); LOG_DBG("%s, bin_pa(0x%lx)\n", __func__, (unsigned long)bin_pa); switch (core) { case 0: default: mva_reset_vector = VPU_MVA_RESET_VECTOR; mva_main_program = VPU_MVA_MAIN_PROGRAM; binpa_reset_vector = bin_pa; binpa_main_program = bin_pa + VPU_OFFSET_MAIN_PROGRAM; binpa_iram_data = bin_pa + VPU_OFFSET_MAIN_PROGRAM_IMEM; break; case 1: mva_reset_vector = VPU2_MVA_RESET_VECTOR; mva_main_program = VPU2_MVA_MAIN_PROGRAM; binpa_reset_vector = bin_pa + VPU_DDR_SHIFT_RESET_VECTOR; binpa_main_program = binpa_reset_vector + VPU_OFFSET_MAIN_PROGRAM; binpa_iram_data = bin_pa + VPU_OFFSET_MAIN_PROGRAM_IMEM + VPU_DDR_SHIFT_IRAM_DATA; break; } LOG_DBG("%s(core:%d), pa resvec/mainpro(0x%lx/0x%lx)\n", __func__, core, (unsigned long)binpa_reset_vector, (unsigned long)binpa_main_program); #ifdef CONFIG_MTK_IOMMU_V2 /*map reset vector*/ vpu_handle = ion_alloc(ion_client, VPU_SIZE_RESET_VECTOR, binpa_reset_vector, ION_HEAP_MULTIMEDIA_PA2MVA_MASK, 0); if (IS_ERR(vpu_handle)) { LOG_ERR("reset-vpu_ion_alloc error %p\n", vpu_handle); return -1; } memset((void *)&vpu_data, 0, sizeof(struct ion_mm_data)); fix_mva = ((unsigned long)VPU_PORT_OF_IOMMU << 24) | ION_FLAG_GET_FIXED_PHYS; vpu_data.get_phys_param.module_id = VPU_PORT_OF_IOMMU; vpu_data.get_phys_param.kernel_handle = vpu_handle; vpu_data.get_phys_param.reserve_iova_start = mva_reset_vector; vpu_data.get_phys_param.reserve_iova_end = IOMMU_VA_END; vpu_data.get_phys_param.phy_addr = fix_mva; vpu_data.get_phys_param.len = ION_FLAG_GET_FIXED_PHYS; vpu_data.mm_cmd = ION_MM_GET_IOVA_EXT; if (ion_kernel_ioctl(ion_client, ION_CMD_MULTIMEDIA, (unsigned long)&vpu_data) < 0) { LOG_ERR("main-config buffer failed.%p -%p\n", ion_client, vpu_handle); ion_free(ion_client, vpu_handle); return -1; } /*map main vector*/ vpu_handle = ion_alloc(ion_client, VPU_SIZE_MAIN_PROGRAM, binpa_main_program, ION_HEAP_MULTIMEDIA_PA2MVA_MASK, 0); if (IS_ERR(vpu_handle)) { LOG_ERR("main-vpu_ion_alloc error %p\n", vpu_handle); return -1; } vpu_data.get_phys_param.kernel_handle = vpu_handle; vpu_data.get_phys_param.phy_addr = fix_mva; vpu_data.get_phys_param.len = ION_FLAG_GET_FIXED_PHYS; vpu_data.get_phys_param.reserve_iova_start = mva_main_program; vpu_data.get_phys_param.reserve_iova_end = IOMMU_VA_END; if (ion_kernel_ioctl(ion_client, ION_CMD_MULTIMEDIA, (unsigned long)&vpu_data) < 0) { LOG_ERR("main-config buffer failed.%p -%p\n", ion_client, vpu_handle); ion_free(ion_client, vpu_handle); return -1; } if (core == 0) { vpu_handle = ion_alloc(ion_client, size_algos, bin_pa + VPU_OFFSET_ALGO_AREA, ION_HEAP_MULTIMEDIA_PA2MVA_MASK, 0); if (IS_ERR(vpu_handle)) { LOG_ERR("main-vpu_ion_alloc error %p\n", vpu_handle); return -1; } vpu_data.get_phys_param.kernel_handle = vpu_handle; vpu_data.get_phys_param.phy_addr = 0; vpu_data.get_phys_param.len = 0; vpu_data.mm_cmd = ION_MM_GET_IOVA; if (ion_kernel_ioctl(ion_client, ION_CMD_MULTIMEDIA, (unsigned long)&vpu_data) < 0) { LOG_ERR("algo-config buffer failed.%p -%p\n", ion_client, vpu_handle); ion_free(ion_client, vpu_handle); return -1; } vpu_service_cores[core].algo_data_mva = vpu_data.get_phys_param.phy_addr; } vpu_handle = ion_alloc(ion_client, VPU_SIZE_MAIN_PROGRAM_IMEM, binpa_iram_data, ION_HEAP_MULTIMEDIA_PA2MVA_MASK, 0); if (IS_ERR(vpu_handle)) { LOG_ERR("main-vpu_ion_alloc error %p\n", vpu_handle); return -1; } vpu_data.get_phys_param.kernel_handle = vpu_handle; vpu_data.get_phys_param.phy_addr = 0; vpu_data.get_phys_param.len = 0; vpu_data.mm_cmd = ION_MM_GET_IOVA; if (ion_kernel_ioctl(ion_client, ION_CMD_MULTIMEDIA, (unsigned long)&vpu_data) < 0) { LOG_ERR("iram-config buffer failed.%p -%p\n", ion_client, vpu_handle); ion_free(ion_client, vpu_handle); return -1; } vpu_service_cores[core].iram_data_mva = vpu_data.get_phys_param.phy_addr; #else /* 1. map reset vector */ sg = &(vpu_service_cores[core].sg_reset_vector); ret = sg_alloc_table(sg, 1, GFP_KERNEL); if (ret) { LOG_ERR("[vpu_%d]fail to allocate sg table[reset]!\n", core); goto out; } LOG_DBG("vpu...sg_alloc_table ok\n"); sg_dma_address(sg->sgl) = binpa_reset_vector; LOG_DBG("vpu...sg_dma_address ok, bin_pa(0x%x)\n", (unsigned int)binpa_reset_vector); sg_dma_len(sg->sgl) = VPU_SIZE_RESET_VECTOR; LOG_DBG("vpu...sg_dma_len ok, VPU_SIZE_RESET_VECTOR(0x%x)\n", VPU_SIZE_RESET_VECTOR); ret = m4u_alloc_mva(m4u_client, VPU_PORT_OF_IOMMU, 0, sg, VPU_SIZE_RESET_VECTOR, M4U_PROT_READ | M4U_PROT_WRITE, M4U_FLAGS_START_FROM, &mva_reset_vector); if (ret) { LOG_ERR("[vpu_%d]fail to allocate mva of reset vecter!\n", core); goto out; } LOG_DBG("vpu...m4u_alloc_mva ok\n"); /* 2. map main program */ sg = &(vpu_service_cores[core].sg_main_program); ret = sg_alloc_table(sg, 1, GFP_KERNEL); if (ret) { LOG_ERR("[vpu_%d]fail to allocate sg table[main]!\n", core); goto out; } LOG_DBG("vpu...sg_alloc_table main_program ok\n"); sg_dma_address(sg->sgl) = binpa_main_program; sg_dma_len(sg->sgl) = VPU_SIZE_MAIN_PROGRAM; ret = m4u_alloc_mva(m4u_client, VPU_PORT_OF_IOMMU, 0, sg, VPU_SIZE_MAIN_PROGRAM, M4U_PROT_READ | M4U_PROT_WRITE, M4U_FLAGS_START_FROM, &mva_main_program); if (ret) { LOG_ERR("fail to allocate mva of main program!\n"); m4u_dealloc_mva(m4u_client, VPU_PORT_OF_IOMMU, mva_main_program); goto out; } LOG_DBG("vpu...m4u_alloc_mva main_program ok, (0x%x/0x%x)\n", (unsigned int)(binpa_main_program), (unsigned int)VPU_SIZE_MAIN_PROGRAM); /* 3. map all algo binary data(src addr for dps to copy) */ /* no need reserved mva, use SG_READY*/ if (core == 0) { sg = &(vpu_service_cores[core].sg_algo_binary_data); ret = sg_alloc_table(sg, 1, GFP_KERNEL); if (ret) { LOG_ERR("[vpu_%d]fail to allocate sg table[reset]!\n", core); goto out; } LOG_DBG("vpu...sg_alloc_table algo_data ok, %s=0x%x\n", "mva_algo_binary_data", mva_algo_binary_data); sg_dma_address(sg->sgl) = bin_pa + VPU_OFFSET_ALGO_AREA; sg_dma_len(sg->sgl) = size_algos; ret = m4u_alloc_mva(m4u_client, VPU_PORT_OF_IOMMU, 0, sg, size_algos, M4U_PROT_READ | M4U_PROT_WRITE, M4U_FLAGS_SG_READY, &mva_algo_binary_data); if (ret) { LOG_ERR("[vpu_%d]fail to allocate %s!\n", core, "mva of reset vecter"); goto out; } vpu_service_cores[core].algo_data_mva = mva_algo_binary_data; LOG_DBG("a vpu va_algo_data pa: 0x%x\n", (unsigned int)(bin_pa + VPU_OFFSET_ALGO_AREA)); LOG_DBG("a vpu va_algo_data: 0x%x/0x%x, size: 0x%x\n", mva_algo_binary_data, (unsigned int)(vpu_service_cores[core].algo_data_mva), (unsigned int)size_algos); } else { vpu_service_cores[core].algo_data_mva = vpu_service_cores[0].algo_data_mva; } /* 4. map main program iram data */ /* no need reserved mva, use SG_READY*/ sg = &(vpu_service_cores[core].sg_iram_data); ret = sg_alloc_table(sg, 1, GFP_KERNEL); if (ret) { LOG_ERR("[vpu_%d]fail to allocate sg table[reset]!\n", core); goto out; } LOG_DBG("vpu...sg_alloc_table iram_data ok, mva_iram_data = 0x%x\n", mva_iram_data); LOG_DBG("a vpu iram pa: 0x%lx\n", (unsigned long)(binpa_iram_data)); sg_dma_address(sg->sgl) = binpa_iram_data; sg_dma_len(sg->sgl) = VPU_SIZE_MAIN_PROGRAM_IMEM; ret = m4u_alloc_mva(m4u_client, VPU_PORT_OF_IOMMU, 0, sg, VPU_SIZE_MAIN_PROGRAM_IMEM, M4U_PROT_READ | M4U_PROT_WRITE, M4U_FLAGS_SG_READY, &mva_iram_data); if (ret) { LOG_ERR("[vpu_%d]fail to allocate mva of iram data!\n", core); goto out; } vpu_service_cores[core].iram_data_mva = (uint64_t)(mva_iram_data); LOG_DBG("a vpu va_iram_data: 0x%x, iram_data_mva: 0x%lx\n", mva_iram_data, (unsigned long)(vpu_service_cores[core].iram_data_mva)); out: #endif #else LOG_DBG("%s bypass\n", __func__); #endif return ret; } #endif int vpu_get_default_algo_num(int core_s, vpu_id_t *algo_num) { int ret = 0; unsigned int core = (unsigned int)core_s; *algo_num = vpu_service_cores[core].default_algo_num; return ret; } int vpu_get_power(int core, bool secure) { int ret = 0; LOG_DBG("[vpu_%d/%d] gp +\n", core, power_counter[core]); mutex_lock(&power_counter_mutex[core]); power_counter[core]++; ret = vpu_boot_up(core, secure); mutex_unlock(&power_counter_mutex[core]); LOG_DBG("[vpu_%d/%d] gp + 2\n", core, power_counter[core]); if (ret == POWER_ON_MAGIC) { mutex_lock(&opp_mutex); if (change_freq_first[core]) { LOG_DBG("[vpu_%d] change freq first(%d)\n", core, change_freq_first[core]); /*mutex_unlock(&opp_mutex);*/ /*mutex_lock(&opp_mutex);*/ if (force_change_dsp_freq[core]) { mutex_unlock(&opp_mutex); /* force change freq while running */ LOG_DBG("vpu_%d force change dsp freq", core); vpu_change_opp(core, OPPTYPE_DSPFREQ); } else { mutex_unlock(&opp_mutex); } mutex_lock(&opp_mutex); //if (force_change_vcore_opp[core]) { if (force_change_vvpu_opp[core]) { mutex_unlock(&opp_mutex); /* vcore change should wait */ LOG_DBG("vpu_%d force change vvpu opp", core); //vpu_change_opp(core, OPPTYPE_VCORE); vpu_change_opp(core, OPPTYPE_VVPU); } else { mutex_unlock(&opp_mutex); } } else { /*mutex_unlock(&opp_mutex);*/ /*mutex_lock(&opp_mutex);*/ //if (force_change_vcore_opp[core]) { if (force_change_vvpu_opp[core]) { mutex_unlock(&opp_mutex); /* vcore change should wait */ LOG_DBG("vpu_%d force change vcore opp", core); //vpu_change_opp(core, OPPTYPE_VCORE); vpu_change_opp(core, OPPTYPE_VVPU); } else { mutex_unlock(&opp_mutex); } mutex_lock(&opp_mutex); if (force_change_dsp_freq[core]) { mutex_unlock(&opp_mutex); /* force change freq while running */ LOG_DBG("vpu_%d force change dsp freq", core); vpu_change_opp(core, OPPTYPE_DSPFREQ); } else { mutex_unlock(&opp_mutex); } } } if (g_vpu_log_level > Log_STATE_MACHINE) apu_get_power_info(); LOG_DBG("[vpu_%d/%d] gp -\n", core, power_counter[core]); enable_apu_bw(0); enable_apu_bw(1); enable_apu_bw(2); enable_apu_latency(0); enable_apu_latency(1); enable_apu_latency(2); #ifdef ENABLE_EARA_QOS if (core == 0) apu_power_count_enable(true, USER_VPU0); else if (core == 1) apu_power_count_enable(true, USER_VPU1); #endif if (ret == POWER_ON_MAGIC) return 0; else return ret; } void vpu_put_power(int core_s, enum VpuPowerOnType type) { unsigned int core = (unsigned int)core_s; LOG_DBG("[vpu_%d/%d] pp +\n", core, power_counter[core]); mutex_lock(&power_counter_mutex[core]); power_counter_work[core].logbackup = 1; if (--power_counter[core] == 0) { switch (type) { case VPT_PRE_ON: LOG_DBG("[vpu_%d] VPT_PRE_ON\n", core); mod_delayed_work(wq, &(power_counter_work[core].my_work), msecs_to_jiffies(10 * PWR_KEEP_TIME_MS)); break; case VPT_IMT_OFF: LOG_INF("[vpu_%d] VPT_IMT_OFF\n", core); mod_delayed_work(wq, &(power_counter_work[core].my_work), msecs_to_jiffies(0)); break; case VPT_SDSP_OFF: power_counter_work[core].logbackup = 0; LOG_INF("[vpu_%d] VPT_IMT_OFF (SDSP)\n", core); mod_delayed_work(wq, &(power_counter_work[core].my_work), msecs_to_jiffies(0)); break; case VPT_ENQUE_ON: default: LOG_DBG("[vpu_%d] VPT_ENQUE_ON\n", core); mod_delayed_work(wq, &(power_counter_work[core].my_work), msecs_to_jiffies(PWR_KEEP_TIME_MS)); break; } } mutex_unlock(&power_counter_mutex[core]); LOG_DBG("[vpu_%d/%d] pp -\n", core, power_counter[core]); } int vpu_set_power(struct vpu_user *user, struct vpu_power *power) { int ret = 0; uint8_t vcore_opp_index = 0xFF; uint8_t vvpu_opp_index = 0xFF; uint8_t dsp_freq_index = 0xFF; unsigned int i = 0, core = 0xFFFFFFFF; mutex_lock(&set_power_mutex); for (i = 0 ; i < MTK_VPU_CORE ; i++) { /*LOG_DBG("debug i(%d), (0x1 << i) (0x%x)", i, (0x1 << i));*/ if (power->core == (0x1 << i)) { core = i; break; } } if (core >= MTK_VPU_CORE) { LOG_ERR("wrong core index (0x%x/%d/%d)", power->core, core, MTK_VPU_CORE); ret = -1; mutex_unlock(&set_power_mutex); return ret; } LOG_INF("[vpu_%d] set power opp:%d, pid=%d, tid=%d\n", core, power->opp_step, user->open_pid, user->open_tgid); if (power->opp_step == 0xFF) { vcore_opp_index = 0xFF; vvpu_opp_index = 0xFF; dsp_freq_index = 0xFF; } else { if (power->opp_step < VPU_MAX_NUM_OPPS) { vcore_opp_index = opps.vcore.opp_map[power->opp_step]; vvpu_opp_index = opps.vvpu.opp_map[power->opp_step]; dsp_freq_index = opps.dspcore[core].opp_map[power->opp_step]; } else { LOG_ERR("wrong opp step (%d)", power->opp_step); ret = -1; mutex_unlock(&set_power_mutex); return ret; } } vpu_opp_check(core, vvpu_opp_index, dsp_freq_index); user->power_opp = power->opp_step; ret = vpu_get_power(core, false); mutex_unlock(&set_power_mutex); mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_IDLE; mutex_unlock(&(vpu_service_cores[core].state_mutex)); /* to avoid power leakage, power on/off need be paired */ vpu_put_power(core, VPT_PRE_ON); LOG_INF("[vpu_%d] %s -\n", core, __func__); return ret; } int vpu_sdsp_get_power(struct vpu_user *user) { int ret = 0; uint8_t vcore_opp_index = 0; /*0~15, 0 is max*/ uint8_t dsp_freq_index = 0; /*0~15, 0 is max*/ int core = 0; if (sdsp_power_counter == 0) { for (core = 0 ; core < MTK_VPU_CORE ; core++) { vpu_opp_check(core, vcore_opp_index, dsp_freq_index); ret = ret | vpu_get_power(core, true); mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_IDLE; mutex_unlock(&(vpu_service_cores[core].state_mutex)); } } sdsp_power_counter++; mod_delayed_work(wq, &sdsp_work, msecs_to_jiffies(SDSP_KEEP_TIME_MS)); LOG_INF("[vpu] %s -\n", __func__); return ret; } int vpu_sdsp_put_power(struct vpu_user *user) { int ret = 0; int core = 0; sdsp_power_counter--; if (sdsp_power_counter == 0) { for (core = 0 ; core < MTK_VPU_CORE ; core++) { while (power_counter[core] != 0) vpu_put_power(core, VPT_SDSP_OFF); while (is_power_on[core] == true) usleep_range(100, 500); LOG_INF("[vpu] power_counter[%d] = %d/%d -\n", core, power_counter[core], is_power_on[core]); } } mod_delayed_work(wq, &sdsp_work, msecs_to_jiffies(0)); LOG_INF("[vpu] %s, sdsp_power_counter = %d -\n", __func__, sdsp_power_counter); return ret; } static void vpu_power_counter_routine(struct work_struct *work) { int core = 0; int logbackup = 0; struct my_struct_t *my_work_core = container_of(work, struct my_struct_t, my_work.work); if (!my_work_core) return; logbackup = my_work_core->logbackup; core = my_work_core->core; LOG_DVFS("vpu_%d counterR (%d)+\n", core, power_counter[core]); mutex_lock(&power_counter_mutex[core]); if (power_counter[core] == 0) vpu_shut_down_ex(core, logbackup); else LOG_DBG("vpu_%d no need this time.\n", core); mutex_unlock(&power_counter_mutex[core]); LOG_DVFS("vpu_%d counterR -", core); } bool vpu_is_idle(int core_s) { bool idle = false; unsigned int core = (unsigned int)core_s; mutex_lock(&(vpu_service_cores[core].state_mutex)); if (vpu_service_cores[core].state == VCT_SHUTDOWN || vpu_service_cores[core].state == VCT_IDLE) idle = true; mutex_unlock(&(vpu_service_cores[core].state_mutex)); LOG_INF("%s vpu_%d, idle = %d, state = %d !!\r\n", __func__, core, idle, vpu_service_cores[core].state); return idle; } int vpu_quick_suspend(int core_s) { unsigned int core = (unsigned int)core_s; LOG_DBG("[vpu_%d] q_suspend +\n", core); mutex_lock(&power_counter_mutex[core]); LOG_INF("[vpu_%d] q_suspend (%d/%d)\n", core, power_counter[core], vpu_service_cores[core].state); if (power_counter[core] == 0) { mutex_unlock(&power_counter_mutex[core]); mutex_lock(&(vpu_service_cores[core].state_mutex)); switch (vpu_service_cores[core].state) { case VCT_SHUTDOWN: case VCT_NONE: /* vpu has already been shut down, do nothing*/ mutex_unlock(&(vpu_service_cores[core].state_mutex)); break; case VCT_IDLE: case VCT_BOOTUP: case VCT_EXECUTING: case VCT_VCORE_CHG: default: mutex_unlock(&(vpu_service_cores[core].state_mutex)); mod_delayed_work(wq, &(power_counter_work[core].my_work), msecs_to_jiffies(0)); break; } } else { mutex_unlock(&power_counter_mutex[core]); } return 0; } static void vpu_opp_keep_routine(struct work_struct *work) { LOG_DVFS("%s flag (%d) +\n", __func__, opp_keep_flag); mutex_lock(&opp_mutex); opp_keep_flag = false; mutex_unlock(&opp_mutex); LOG_DVFS("%s flag (%d) -\n", __func__, opp_keep_flag); } static void vpu_sdsp_routine(struct work_struct *work) { if (sdsp_power_counter != 0) { LOG_INF("%s long time not unlock!!! -\n", __func__); LOG_ERR("%s long time not unlock error!!! -\n", __func__); } else { LOG_INF("%s sdsp_power_counter is correct!!! -\n", __func__); } } int vpu_init_hw(int core_s, struct vpu_device *device) { int ret, i, j; int param; unsigned int core = (unsigned int)core_s; struct vpu_shared_memory_param mem_param; vpu_dump_exception = 0; vpu_service_cores[core].vpu_base = device->vpu_base[core]; vpu_service_cores[core].bin_base = device->bin_base; #ifdef MTK_VPU_EMULATOR vpu_request_emulator_irq(device->irq_num[core], vpu_isr_handler); #else #ifdef CONFIG_MTK_M4U if (!m4u_client) m4u_client = m4u_create_client(); #endif if (!ion_client) ion_client = ion_client_create(g_ion_device, "vpu"); ret = vpu_map_mva_of_bin(core, device->bin_pa); if (ret) { LOG_ERR("[vpu_%d]fail to map binary data!\n", core); goto out; } vpu_total_algo_num(core); ret = request_irq(device->irq_num[core], (irq_handler_t)VPU_ISR_CB_TBL[core].isr_fp, device->irq_trig_level, (const char *)VPU_ISR_CB_TBL[core].device_name, NULL); if (ret) { LOG_ERR("[vpu_%d]fail to request vpu irq!\n", core); goto out; } #endif sdsp_power_counter = 0; if (core == 0) { init_waitqueue_head(&cmd_wait); mutex_init(&lock_mutex); init_waitqueue_head(&lock_wait); mutex_init(&opp_mutex); mutex_init(&power_lock_mutex); mutex_init(&set_power_mutex); init_waitqueue_head(&waitq_change_vcore); init_waitqueue_head(&waitq_do_core_executing); is_locked = false; max_vcore_opp = 0; max_vvpu_opp = 0; max_dsp_freq = 0; opp_keep_flag = false; vpu_dev = device; is_power_debug_lock = false; #ifdef MTK_VPU_FPGA_PORTING vpu_dev->vpu_hw_support[0] = true; vpu_dev->vpu_hw_support[1] = true; #else efuse_data = (get_devinfo_with_index(3) & 0xC00) >> 10; LOG_INF("efuse_data: efuse_data(0x%x)\n", efuse_data); switch (efuse_data) { case 0x3: /*b11*/ vpu_dev->vpu_hw_support[0] = false; vpu_dev->vpu_hw_support[1] = false; break; case 0x0: default: vpu_dev->vpu_hw_support[0] = true; vpu_dev->vpu_hw_support[1] = true; break; case 0x2: /*b10*/ vpu_dev->vpu_hw_support[0] = true; vpu_dev->vpu_hw_support[1] = false; break; } get_segment_from_efuse(); #endif for (i = 0 ; i < MTK_VPU_CORE ; i++) { mutex_init(&(power_mutex[i])); mutex_init(&(power_counter_mutex[i])); power_counter[i] = 0; power_counter_work[i].core = i; is_power_on[i] = false; force_change_vcore_opp[i] = false; force_change_vvpu_opp[i] = false; force_change_dsp_freq[i] = false; change_freq_first[i] = false; exception_isr_check[i] = false; INIT_DELAYED_WORK(&(power_counter_work[i].my_work), vpu_power_counter_routine); #ifdef MET_VPU_LOG /* Init ftrace dumpwork information */ spin_lock_init(&(ftrace_dump_work[i].my_lock)); INIT_LIST_HEAD(&(ftrace_dump_work[i].list)); INIT_WORK(&(ftrace_dump_work[i].my_work), vpu_dump_ftrace_workqueue); #endif #ifdef CONFIG_PM_SLEEP if (i == 0) { /* wakeup_source_init(&(vpu_wake_lock[i]), "vpu_wakelock_0"); */ vpu_wake_lock[i] = wakeup_source_register( NULL, "vpu_wakelock_0"); /* if (!vpu_wake_lock[i]) pr_info("%s: vpu%d: wakeup_source_register fail\n", __func__, vd->id); */ } else { /* vpu_wake_lock[i] = wakeup_source_register( device->dev, "vpu_wakelock_1"); if (!vpu_wake_lock[i]) pr_info("%s: vpu%d: wakeup_source_register fail\n", __func__, vd->id); */ } #endif if (vpu_dev->vpu_hw_support[i]) { param = i; vpu_service_cores[i].thread_var = i; if (i == 0) { vpu_service_cores[i].srvc_task = kthread_create(vpu_service_routine, &(vpu_service_cores[i].thread_var), "vpu0"); } else { vpu_service_cores[i].srvc_task = kthread_create(vpu_service_routine, &(vpu_service_cores[i].thread_var), "vpu1"); } if (IS_ERR(vpu_service_cores[i].srvc_task)) { ret = PTR_ERR(vpu_service_cores[i].srvc_task); goto out; } #ifdef MET_VPU_LOG /* to save correct pid */ ftrace_dump_work[i].pid = vpu_service_cores[i].srvc_task->pid; #endif wake_up_process(vpu_service_cores[i].srvc_task); } mutex_init(&(vpu_service_cores[i].cmd_mutex)); vpu_service_cores[i].is_cmd_done = false; mutex_init(&(vpu_service_cores[i].state_mutex)); vpu_service_cores[i].state = VCT_SHUTDOWN; /* working buffer */ /* no need in reserved region */ mem_param.require_pa = true; mem_param.require_va = true; mem_param.size = VPU_SIZE_WORK_BUF; mem_param.fixed_addr = 0; ret = vpu_alloc_shared_memory( &(vpu_service_cores[i].work_buf), &mem_param); vpu_service_cores[i].work_buf_logsize = 0; LOG_INF("core(%d):work_buf va (0x%lx),pa(0x%x)\n", i, (unsigned long) (vpu_service_cores[i].work_buf->va), vpu_service_cores[i].work_buf->pa); if (ret) { LOG_ERR("core(%d):fail to allocate %s!\n", i, "working buffer"); goto out; } /* execution kernel library */ /* need in reserved region, set end as the end of * reserved address, m4u use start-from */ mem_param.require_pa = true; mem_param.require_va = true; mem_param.size = VPU_SIZE_ALGO_KERNEL_LIB; switch (i) { case 0: default: mem_param.fixed_addr = VPU_MVA_KERNEL_LIB; break; case 1: mem_param.fixed_addr = VPU2_MVA_KERNEL_LIB; break; } ret = vpu_alloc_shared_memory( &(vpu_service_cores[i].exec_kernel_lib), &mem_param); LOG_INF("core(%d):kernel_lib va (0x%lx),pa(0x%x)\n", i, (unsigned long) (vpu_service_cores[i].exec_kernel_lib->va), vpu_service_cores[i].exec_kernel_lib->pa); if (ret) { LOG_ERR("core(%d):fail to allocate %s!\n", i, "kernel_lib buffer"); goto out; } } /* multi-core shared */ /* need in reserved region, set end as the end of reserved * address, m4u use start-from */ mem_param.require_pa = true; mem_param.require_va = true; mem_param.size = VPU_SIZE_SHARED_DATA; mem_param.fixed_addr = VPU_MVA_SHARED_DATA; ret = vpu_alloc_shared_memory(&(core_shared_data), &mem_param); LOG_DBG("shared_data va (0x%lx),pa(0x%x)", (unsigned long)(core_shared_data->va), core_shared_data->pa); if (ret) { LOG_ERR("fail to allocate working buffer!\n"); goto out; } wq = create_workqueue("vpu_wq"); g_func_mask = 0x0; #ifdef ENABLE_EARA_QOS vpu_qos_counter_init(); #endif #if 0 /* define the steps and OPP */ #define DEFINE_VPU_STEP(step, m, v0, v1, v2, v3, v4, v5, v6, v7) \ { \ opps.step.index = m - 1; \ opps.step.count = m; \ opps.step.values[0] = v0; \ opps.step.values[1] = v1; \ opps.step.values[2] = v2; \ opps.step.values[3] = v3; \ opps.step.values[4] = v4; \ opps.step.values[5] = v5; \ opps.step.values[6] = v6; \ opps.step.values[7] = v7; \ } #define DEFINE_VPU_OPP(i, v0, v1, v2, v3, v4) \ { \ opps.vcore.opp_map[i] = v0; \ opps.dsp.opp_map[i] = v1; \ opps.dspcore[0].opp_map[i] = v2; \ opps.dspcore[1].opp_map[i] = v3; \ opps.ipu_if.opp_map[i] = v4; \ } DEFINE_VPU_STEP(vcore, 2, 800000, 725000, 0, 0, 0, 0, 0, 0); DEFINE_VPU_STEP(dsp, 8, 525000, 450000, 416000, 364000, 312000, 273000, 208000, 182000); DEFINE_VPU_STEP(dspcore[0], 8, 525000, 450000, 416000, 364000, 312000, 273000, 208000, 182000); DEFINE_VPU_STEP(dspcore[1], 8, 525000, 450000, 416000, 364000, 312000, 273000, 208000, 182000); DEFINE_VPU_STEP(ipu_if, 8, 525000, 450000, 416000, 364000, 312000, 273000, 208000, 182000); /* default freq */ DEFINE_VPU_OPP(0, 0, 0, 0, 0, 0); DEFINE_VPU_OPP(1, 0, 1, 1, 1, 1); DEFINE_VPU_OPP(2, 0, 2, 2, 2, 2); DEFINE_VPU_OPP(3, 0, 3, 3, 3, 3); DEFINE_VPU_OPP(4, 1, 4, 4, 4, 4); DEFINE_VPU_OPP(5, 1, 5, 5, 5, 5); DEFINE_VPU_OPP(6, 1, 6, 6, 6, 6); DEFINE_VPU_OPP(7, 1, 7, 7, 7, 7); /* default low opp */ opps.count = 8; opps.index = 4; /* user space usage*/ opps.vcore.index = 1; opps.dsp.index = 4; opps.dspcore[0].index = 4; opps.dspcore[1].index = 4; opps.ipu_if.index = 4; #undef DEFINE_VPU_OPP #undef DEFINE_VPU_STEP #else /* define the steps and OPP */ #define DEFINE_APU_STEP(step, m, v0, v1, v2, v3, \ v4, v5, v6, v7, v8, v9, \ v10, v11, v12, v13, v14, v15) \ { \ opps.step.index = m - 1; \ opps.step.count = m; \ opps.step.values[0] = v0; \ opps.step.values[1] = v1; \ opps.step.values[2] = v2; \ opps.step.values[3] = v3; \ opps.step.values[4] = v4; \ opps.step.values[5] = v5; \ opps.step.values[6] = v6; \ opps.step.values[7] = v7; \ opps.step.values[8] = v8; \ opps.step.values[9] = v9; \ opps.step.values[10] = v10; \ opps.step.values[11] = v11; \ opps.step.values[12] = v12; \ opps.step.values[13] = v13; \ opps.step.values[14] = v14; \ opps.step.values[15] = v15; \ } #define DEFINE_APU_OPP(i, v0, v1, v2, v3, v4) \ { \ opps.vvpu.opp_map[i] = v0; \ opps.dsp.opp_map[i] = v1; \ opps.dspcore[0].opp_map[i] = v2; \ opps.dspcore[1].opp_map[i] = v3; \ opps.ipu_if.opp_map[i] = v4; \ } DEFINE_APU_STEP(vcore, 3, 825000, 725000, 650000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); DEFINE_APU_STEP(vvpu, 4, 850000, 825000, 725000, 650000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); DEFINE_APU_STEP(dsp, 16, 750000, 700000, 624000, 594000, 560000, 525000, 450000, 416000, 364000, 312000, 273000, 208000, 137000, 104000, 52000, 26000); DEFINE_APU_STEP(dspcore[0], 16, 750000, 700000, 624000, 594000, 560000, 525000, 450000, 416000, 364000, 312000, 273000, 208000, 137000, 104000, 52000, 26000); DEFINE_APU_STEP(dspcore[1], 16, 750000, 700000, 624000, 594000, 560000, 525000, 450000, 416000, 364000, 312000, 273000, 208000, 137000, 104000, 52000, 26000); DEFINE_APU_STEP(ipu_if, 16, 624000, 624000, 624000, 594000, 560000, 525000, 450000, 416000, 364000, 312000, 273000, 208000, 137000, 104000, 52000, 26000); /* default freq */ DEFINE_APU_OPP(0, 0, 0, 0, 0, 0); DEFINE_APU_OPP(1, 1, 1, 1, 1, 1); DEFINE_APU_OPP(2, 1, 2, 2, 2, 2); DEFINE_APU_OPP(3, 1, 3, 3, 3, 3); DEFINE_APU_OPP(4, 1, 4, 4, 4, 4); DEFINE_APU_OPP(5, 2, 5, 5, 5, 5); DEFINE_APU_OPP(6, 2, 6, 6, 6, 6); DEFINE_APU_OPP(7, 2, 7, 7, 7, 7); DEFINE_APU_OPP(8, 2, 8, 8, 8, 8); DEFINE_APU_OPP(9, 3, 9, 9, 9, 9); DEFINE_APU_OPP(10, 3, 10, 10, 10, 10); DEFINE_APU_OPP(11, 3, 11, 11, 11, 11); DEFINE_APU_OPP(12, 3, 12, 12, 12, 12); DEFINE_APU_OPP(13, 3, 13, 13, 13, 13); DEFINE_APU_OPP(14, 3, 14, 14, 14, 14); DEFINE_APU_OPP(15, 3, 15, 15, 15, 15); /* default low opp */ opps.count = 16; opps.index = 4; /* user space usage*/ opps.vvpu.index = 1; opps.dsp.index = 9; opps.dspcore[0].index = 9; opps.dspcore[1].index = 9; opps.ipu_if.index = 9; #undef DEFINE_APU_OPP #undef DEFINE_APU_STEP #endif ret = vpu_prepare_regulator_and_clock(vpu_dev->dev[core]); if (ret) { LOG_ERR("[vpu_%d]fail to prepare regulator or clk!\n", core); goto out; } /* pmqos */ #ifdef ENABLE_PMQOS for (i = 0 ; i < MTK_VPU_CORE ; i++) { mtk_pm_qos_add_request(&vpu_qos_vvpu_request[i], MTK_PM_QOS_VVPU_OPP, MTK_PM_QOS_VVPU_OPP_DEFAULT_VALUE); mtk_pm_qos_update_request(&vpu_qos_vvpu_request[i], VVPU_OPP_3); } #endif } /*init vpu lock power struct*/ for (i = 0 ; i < VPU_OPP_PRIORIYY_NUM ; i++) { for (j = 0 ; j < MTK_VPU_CORE ; j++) { lock_power[i][j].core = 0; lock_power[i][j].max_boost_value = 100; lock_power[i][j].min_boost_value = 0; lock_power[i][j].lock = false; lock_power[i][j].priority = NORMAL; min_opp[j] = VPU_MAX_NUM_OPPS-1; max_opp[j] = 0; min_boost[j] = 0; max_boost[j] = 100; } } vpu_init_done = 1; return 0; out: for (i = 0 ; i < MTK_VPU_CORE ; i++) { if (vpu_service_cores[i].srvc_task != NULL) { kthread_stop(vpu_service_cores[i].srvc_task); vpu_service_cores[i].srvc_task = NULL; } if (vpu_service_cores[i].work_buf) vpu_free_shared_memory(vpu_service_cores[i].work_buf); } return ret; } int vpu_uninit_hw(void) { int i; for (i = 0 ; i < MTK_VPU_CORE ; i++) { cancel_delayed_work(&(power_counter_work[i].my_work)); if (vpu_service_cores[i].srvc_task != NULL) { kthread_stop(vpu_service_cores[i].srvc_task); vpu_service_cores[i].srvc_task = NULL; } if (vpu_service_cores[i].work_buf) { vpu_free_shared_memory(vpu_service_cores[i].work_buf); vpu_service_cores[i].work_buf = NULL; } } cancel_delayed_work(&opp_keep_work); /* pmqos */ #ifdef ENABLE_PMQOS for (i = 0 ; i < MTK_VPU_CORE ; i++) mtk_pm_qos_remove_request(&vpu_qos_vvpu_request[i]); #endif vpu_unprepare_regulator_and_clock(); #ifdef CONFIG_MTK_m4U if (m4u_client) { m4u_destroy_client(m4u_client); m4u_client = NULL; } #endif if (ion_client) { ion_client_destroy(ion_client); ion_client = NULL; } if (wq) { flush_workqueue(wq); destroy_workqueue(wq); wq = NULL; } vpu_init_done = 0; #ifdef ENABLE_EARA_QOS vpu_qos_counter_destroy(); #endif return 0; } static int vpu_check_precond(int core) { uint32_t status; size_t i; /* wait 1 seconds, if not ready or busy */ for (i = 0; i < 50; i++) { status = vpu_read_field(core, FLD_XTENSA_INFO00); switch (status) { case VPU_STATE_READY: case VPU_STATE_IDLE: case VPU_STATE_ERROR: return 0; case VPU_STATE_NOT_READY: case VPU_STATE_BUSY: msleep(20); break; case VPU_STATE_TERMINATED: return -EBADFD; } } LOG_ERR("core(%d) still busy(%d) after wait 1 second.\n", core, status); return -EBUSY; } static int vpu_check_postcond(int core) { uint32_t status = vpu_read_field(core, FLD_XTENSA_INFO00); LOG_DBG("%s (0x%x)\n", __func__, status); switch (status) { case VPU_STATE_READY: case VPU_STATE_IDLE: return 0; case VPU_STATE_NOT_READY: case VPU_STATE_ERROR: return -EIO; case VPU_STATE_BUSY: return -EBUSY; case VPU_STATE_TERMINATED: return -EBADFD; default: return -EINVAL; } } int vpu_hw_enable_jtag(bool enabled) { int ret = 0; int TEMP_CORE = 0; vpu_get_power(TEMP_CORE, false); vpu_write_field(TEMP_CORE, FLD_SPNIDEN, enabled); vpu_write_field(TEMP_CORE, FLD_SPIDEN, enabled); vpu_write_field(TEMP_CORE, FLD_NIDEN, enabled); vpu_write_field(TEMP_CORE, FLD_DBG_EN, enabled); vpu_put_power(TEMP_CORE, VPT_ENQUE_ON); return ret; } int vpu_hw_boot_sequence(int core_s) { int ret; uint64_t ptr_ctrl; uint64_t ptr_reset; uint64_t ptr_axi_0; uint64_t ptr_axi_1; unsigned int reg_value = 0; bool is_hw_fail = true; unsigned int core = (unsigned int)core_s; if (core >= MTK_VPU_CORE) { LOG_DBG("invalid core =%d\n", core); return -EINVAL; } vpu_trace_begin("%s", __func__); LOG_INF("[vpu_%d] boot-seq core(%d)\n", core, core); LOG_DBG("CTRL(0x%x)\n", vpu_read_reg32(vpu_service_cores[core].vpu_base, CTRL_BASE_OFFSET + 0x110)); LOG_DBG("XTENSA_INT(0x%x)\n", vpu_read_reg32(vpu_service_cores[core].vpu_base, CTRL_BASE_OFFSET + 0x114)); LOG_DBG("CTL_XTENSA_INT(0x%x)\n", vpu_read_reg32(vpu_service_cores[core].vpu_base, CTRL_BASE_OFFSET + 0x118)); LOG_DBG("CTL_XTENSA_INT_CLR(0x%x)\n", vpu_read_reg32(vpu_service_cores[core].vpu_base, CTRL_BASE_OFFSET + 0x11C)); lock_command(core, 0x0); ptr_ctrl = vpu_service_cores[core].vpu_base + g_vpu_reg_descs[REG_CTRL].offset; ptr_reset = vpu_service_cores[core].vpu_base + g_vpu_reg_descs[REG_SW_RST].offset; ptr_axi_0 = vpu_service_cores[core].vpu_base + g_vpu_reg_descs[REG_AXI_DEFAULT0].offset; ptr_axi_1 = vpu_service_cores[core].vpu_base + g_vpu_reg_descs[REG_AXI_DEFAULT1].offset; /* 1. write register */ /* set specific address for reset vector in external boot */ reg_value = vpu_read_field(core, FLD_CORE_XTENSA_ALTRESETVEC); LOG_DBG("vpu bf ALTRESETVEC (0x%x), RV(0x%x)\n", reg_value, VPU_MVA_RESET_VECTOR); switch (core) { case 0: default: vpu_write_field(core, FLD_CORE_XTENSA_ALTRESETVEC, VPU_MVA_RESET_VECTOR); break; case 1: vpu_write_field(core, FLD_CORE_XTENSA_ALTRESETVEC, VPU2_MVA_RESET_VECTOR); break; } reg_value = vpu_read_field(core, FLD_CORE_XTENSA_ALTRESETVEC); LOG_DBG("vpu af ALTRESETVEC (0x%x), RV(0x%x)\n", reg_value, VPU_MVA_RESET_VECTOR); VPU_SET_BIT(ptr_ctrl, 31); /* csr_p_debug_enable */ VPU_SET_BIT(ptr_ctrl, 26); /* debug interface cock gated enable */ VPU_SET_BIT(ptr_ctrl, 19); /* force to boot based on X_ALTRESETVEC */ VPU_SET_BIT(ptr_ctrl, 23); /* RUN_STALL pull up */ VPU_SET_BIT(ptr_ctrl, 17); /* pif gated enable */ VPU_CLR_BIT(ptr_ctrl, 29); VPU_CLR_BIT(ptr_ctrl, 28); VPU_CLR_BIT(ptr_ctrl, 27); VPU_SET_BIT(ptr_reset, 12); /* OCD_HALT_ON_RST pull up */ ndelay(27); /* wait for 27ns */ VPU_CLR_BIT(ptr_reset, 12); /* OCD_HALT_ON_RST pull down */ /* set PRID */ vpu_write_field(core, FLD_PRID, core); VPU_SET_BIT(ptr_reset, 4); /* B_RST pull up */ VPU_SET_BIT(ptr_reset, 8); /* D_RST pull up */ ndelay(27); /* wait for 27ns */ VPU_CLR_BIT(ptr_reset, 4); /* B_RST pull down */ VPU_CLR_BIT(ptr_reset, 8); /* D_RST pull down */ ndelay(27); /* wait for 27ns */ /* pif gated disable, to prevent unknown propagate to BUS */ VPU_CLR_BIT(ptr_ctrl, 17); #ifndef BYPASS_M4U_DBG /*Setting for mt6779*/ VPU_SET_BIT(ptr_axi_0, 21); VPU_SET_BIT(ptr_axi_0, 26); VPU_SET_BIT(ptr_axi_1, 3); VPU_SET_BIT(ptr_axi_1, 8); #endif /* default set pre-ultra instead of ultra */ VPU_SET_BIT(ptr_axi_0, 28); if (g_vpu_log_level > VpuLogThre_PERFORMANCE) { LOG_INF("[vpu_%d] REG_AXI_DEFAULT0(0x%x)\n", core, vpu_read_reg32(vpu_service_cores[core].vpu_base, CTRL_BASE_OFFSET + 0x13C)); } LOG_DBG("[vpu_%d] REG_AXI_DEFAULT1(0x%x)\n", core, vpu_read_reg32(vpu_service_cores[core].vpu_base, CTRL_BASE_OFFSET + 0x140)); /* 2. trigger to run */ LOG_DBG("vpu dsp:running (%d/0x%x)\n", core, vpu_read_field(core, FLD_SRAM_CONFIGURE)); vpu_trace_begin("dsp:running"); /* RUN_STALL pull down */ VPU_CLR_BIT(ptr_ctrl, 23); /* 3. wait until done */ ret = wait_command(core); if (ret == -ERESTARTSYS) is_hw_fail = false; ret |= vpu_check_postcond(core); /* handle for err/exception isr */ /* RUN_STALL pull up to avoid fake cmd */ VPU_SET_BIT(ptr_ctrl, 23); vpu_trace_end(); if (ret) { vpu_err_hnd(is_hw_fail, core, NULL, "VPU Timeout", "vpu%d: Boot-Up Timeout", core); goto out; } /* 4. check the result of boot sequence */ ret = vpu_check_postcond(core); if (ret) { LOG_ERR("[vpu_%d]fail to boot vpu!\n", core); goto out; } out: unlock_command(core); vpu_trace_end(); vpu_write_field(core, FLD_APMCU_INT, 1); LOG_INF("[vpu_%d] hw_boot_sequence with clr INT-\n", core); return ret; } #ifdef MET_VPU_LOG static int vpu_hw_set_log_option(int core) { int ret; /* update log enable and mpu enable */ lock_command(core, VPU_CMD_SET_FTRACE_LOG); /* set vpu internal log enable,disable */ if (g_func_mask & VFM_ROUTINE_PRT_SYSLOG) { vpu_write_field(core, FLD_XTENSA_INFO01, VPU_CMD_SET_FTRACE_LOG); vpu_write_field(core, FLD_XTENSA_INFO05, 1); } else { vpu_write_field(core, FLD_XTENSA_INFO01, VPU_CMD_SET_FTRACE_LOG); vpu_write_field(core, FLD_XTENSA_INFO05, 0); } /* set vpu internal log level */ vpu_write_field(core, FLD_XTENSA_INFO06, g_vpu_internal_log_level); /* clear info18 */ vpu_write_field(core, FLD_XTENSA_INFO18, 0); vpu_write_field(core, FLD_RUN_STALL, 0); /* RUN_STALL pull down */ vpu_write_field(core, FLD_CTL_INT, 1); ret = wait_command(core); vpu_write_field(core, FLD_RUN_STALL, 1); /* RUN_STALL pull up to avoid fake cmd */ /* 4. check the result */ ret = vpu_check_postcond(core); /*CHECK_RET("[vpu_%d]fail to set debug!\n", core);*/ unlock_command(core); return ret; } #endif int vpu_hw_set_debug(int core_s) { int ret; struct timespec now; unsigned int device_version = 0x0; bool is_hw_fail = true; unsigned int core = (unsigned int)core_s; LOG_DBG("%s (%d)+\n", __func__, core); vpu_trace_begin("%s", __func__); lock_command(core, VPU_CMD_SET_DEBUG); /* 1. set debug */ getnstimeofday(&now); vpu_write_field(core, FLD_XTENSA_INFO01, VPU_CMD_SET_DEBUG); vpu_write_field(core, FLD_XTENSA_INFO19, vpu_service_cores[core].iram_data_mva); vpu_write_field(core, FLD_XTENSA_INFO21, vpu_service_cores[core].work_buf->pa + VPU_OFFSET_LOG); vpu_write_field(core, FLD_XTENSA_INFO22, VPU_SIZE_LOG_BUF); vpu_write_field(core, FLD_XTENSA_INFO23, now.tv_sec * 1000000 + now.tv_nsec / 1000); vpu_write_field(core, FLD_XTENSA_INFO29, HOST_VERSION); vpu_write_field(core, FLD_XTENSA_INFO30, vpu_service_cores[core].work_buf_logsize); LOG_INF("[vpu_%d] %s=(0x%lx), INFO01(0x%x), 23(%d)\n", core, "work_buf->pa + VPU_OFFSET_LOG", (unsigned long)(vpu_service_cores[core].work_buf->pa + VPU_OFFSET_LOG), vpu_read_field(core, FLD_XTENSA_INFO01), vpu_read_field(core, FLD_XTENSA_INFO23)); LOG_DBG("vpu_set ok, running\n"); /* 2. trigger interrupt */ vpu_trace_begin("dsp:running"); /* RUN_STALL pull down*/ vpu_write_field(core, FLD_RUN_STALL, 0); vpu_write_field(core, FLD_CTL_INT, 1); LOG_DBG("debug timestamp: %.2lu:%.2lu:%.2lu:%.6lu\n", (now.tv_sec / 3600) % (24), (now.tv_sec / 60) % (60), now.tv_sec % 60, now.tv_nsec / 1000); /* 3. wait until done */ ret = wait_command(core); if (ret == -ERESTARTSYS) is_hw_fail = false; ret |= vpu_check_postcond(core); /* RUN_STALL pull up to avoid fake cmd */ vpu_write_field(core, FLD_RUN_STALL, 1); vpu_trace_end(); /*3-additional. check vpu device/host version is matched or not*/ device_version = vpu_read_field(core, FLD_XTENSA_INFO20); if ((int)device_version < (int)HOST_VERSION) { LOG_ERR("[vpu_%d] %s device(0x%x) v.s host(0x%x)\n", core, "wrong vpu version", vpu_read_field(core, FLD_XTENSA_INFO20), vpu_read_field(core, FLD_XTENSA_INFO29)); ret = -EIO; } if (ret) { vpu_err_hnd(is_hw_fail, core, NULL, "VPU Timeout", "vpu%d: Set Debug Timeout", core); goto out; } /* 4. check the result */ ret = vpu_check_postcond(core); if (ret) { LOG_ERR("[vpu_%d]fail to set debug!\n", core); goto out; } out: unlock_command(core); #ifdef MET_VPU_LOG /* to set vpu ftrace log */ ret = vpu_hw_set_log_option(core); #endif vpu_trace_end(); LOG_INF("[vpu_%d] hw_set_debug - version check(0x%x/0x%x)\n", core, vpu_read_field(core, FLD_XTENSA_INFO20), vpu_read_field(core, FLD_XTENSA_INFO29)); return ret; } int vpu_get_name_of_algo(int core_s, int id, char **name) { int i; unsigned int tmp = (unsigned int)id; struct vpu_image_header *header; unsigned int core = (unsigned int)core_s; header = (struct vpu_image_header *) ((uintptr_t)vpu_service_cores[core].bin_base + (VPU_OFFSET_IMAGE_HEADERS)); for (i = 0; i < VPU_NUMS_IMAGE_HEADER; i++) { if (tmp > header[i].algo_info_count) { tmp -= header[i].algo_info_count; continue; } *name = header[i].algo_infos[tmp - 1].name; return 0; } *name = NULL; LOG_ERR("algo is not existed, id=%d\n", id); return -ENOENT; } int vpu_total_algo_num(int core_s) { int i; int total = 0; struct vpu_image_header *header; unsigned int core = (unsigned int)core_s; LOG_DBG("[vpu] %s +\n", __func__); header = (struct vpu_image_header *) ((uintptr_t)vpu_service_cores[core].bin_base + (VPU_OFFSET_IMAGE_HEADERS)); for (i = 0; i < VPU_NUMS_IMAGE_HEADER; i++) total += header[i].algo_info_count; vpu_service_cores[core].default_algo_num = total; return total; }; int vpu_get_entry_of_algo(int core_s, char *name, int *id, unsigned int *mva, int *length) { int i, j; int s = 1; unsigned int coreMagicNum; struct vpu_algo_info *algo_info; struct vpu_image_header *header; unsigned int core = (unsigned int)core_s; LOG_DBG("[vpu] %s +\n", __func__); /* coreMagicNum = ( 0x60 | (0x01 << core) ); */ /* ignore vpu version */ coreMagicNum = (0x01 << core); header = (struct vpu_image_header *) ((uintptr_t)vpu_service_cores[core].bin_base + (VPU_OFFSET_IMAGE_HEADERS)); for (i = 0; i < VPU_NUMS_IMAGE_HEADER; i++) { for (j = 0; j < header[i].algo_info_count; j++) { algo_info = &header[i].algo_infos[j]; LOG_INF("%s: %s/%s, %s:0x%x, %s:%d, %s:0x%x, 0x%x\n", "algo name", name, algo_info->name, "core info", (algo_info->vpu_core), "input core", core, "magicNum", coreMagicNum, algo_info->vpu_core & coreMagicNum); /* CHRISTODO */ if ((strcmp(name, algo_info->name) == 0) && (algo_info->vpu_core & coreMagicNum)) { LOG_INF("[%d] algo_info->offset(0x%x)/0x%x", core, algo_info->offset, (unsigned int) (vpu_service_cores[core].algo_data_mva)); *mva = algo_info->offset - VPU_OFFSET_ALGO_AREA + vpu_service_cores[core].algo_data_mva; LOG_INF("[%d] *mva(0x%x/0x%lx), s(%d)", core, *mva, (unsigned long)(*mva), s); *length = algo_info->length; *id = s; return 0; } s++; } } *id = 0; LOG_ERR("algo is not existed, name=%s\n", name); return -ENOENT; }; int vpu_ext_be_busy(void) { int ret; /* CHRISTODO */ int TEMP_CORE = 0; lock_command(TEMP_CORE, VPU_CMD_EXT_BUSY); /* 1. write register */ vpu_write_field(TEMP_CORE, FLD_XTENSA_INFO01, VPU_CMD_EXT_BUSY); /* 2. trigger interrupt */ vpu_write_field(TEMP_CORE, FLD_CTL_INT, 1); /* 3. wait until done */ ret = wait_command(TEMP_CORE); unlock_command(TEMP_CORE); return ret; } int vpu_debug_func_core_state(int core_s, enum VpuCoreState state) { unsigned int core = (unsigned int)core_s; mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = state; mutex_unlock(&(vpu_service_cores[core].state_mutex)); return 0; } enum MTK_APUSYS_KERNEL_OP { MTK_VPU_SMC_INIT = 0, MTK_APUSYS_KERNEL_OP_NUM }; int vpu_boot_up(int core_s, bool secure) { int ret = 0; unsigned int core = (unsigned int)core_s; struct arm_smccc_res res; /*secure flag is for sdsp force shut down*/ LOG_DBG("[vpu_%d] boot_up +\n", core); mutex_lock(&power_mutex[core]); LOG_DBG("[vpu_%d] is_power_on(%d)\n", core, is_power_on[core]); if (is_power_on[core]) { if (secure) { if (power_counter[core] != 1) LOG_ERR("vpu_%d power counter %d > 1 .\n", core, power_counter[core]); LOG_WRN("force shut down for sdsp..\n"); mutex_unlock(&power_mutex[core]); vpu_shut_down(core); mutex_lock(&power_mutex[core]); } else { mutex_unlock(&power_mutex[core]); mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_BOOTUP; mutex_unlock(&(vpu_service_cores[core].state_mutex)); wake_up_interruptible(&waitq_change_vcore); return POWER_ON_MAGIC; } } LOG_DBG("[vpu_%d] boot_up flag2\n", core); vpu_trace_begin("%s", __func__); mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_BOOTUP; mutex_unlock(&(vpu_service_cores[core].state_mutex)); wake_up_interruptible(&waitq_change_vcore); ret = vpu_enable_regulator_and_clock(core); if (ret) { LOG_ERR("[vpu_%d]fail to enable regulator or clock\n", core); goto out; } if (!secure) { arm_smccc_smc(MTK_SIP_APUSYS_CONTROL, MTK_VPU_SMC_INIT, 0, 0, 0, 0, 0, 0, &res); ret = vpu_hw_boot_sequence(core); if (ret) { LOG_ERR("[vpu_%d]fail to do boot sequence\n", core); goto out; } LOG_DBG("[vpu_%d] vpu_hw_boot_sequence done\n", core); ret = vpu_hw_set_debug(core); if (ret) { LOG_ERR("[vpu_%d]fail to set debug\n", core); goto out; } } LOG_DBG("[vpu_%d] vpu_hw_set_debug done\n", core); #ifdef MET_POLLING_MODE if (g_func_mask & VFM_ROUTINE_PRT_SYSLOG) { ret = vpu_profile_state_set(core, 1); if (ret) { LOG_ERR("[vpu_%d] fail to profile_state_set 1\n", core); goto out; } } #endif out: #if 0 /* control on/off outside the via get_power/put_power */ if (ret) { ret = vpu_disable_regulator_and_clock(core); if (ret) { LOG_ERR("[vpu_%d]fail to disable regulator and clk\n", core); goto out; } } #endif vpu_trace_end(); mutex_unlock(&power_mutex[core]); #ifdef MTK_PERF_OBSERVER if (!ret) { struct pob_xpufreq_info pxi; pxi.id = core; pxi.opp = opps.dspcore[core].index; pob_xpufreq_update(POB_XPUFREQ_VPU, &pxi); } #endif #ifdef ENABLE_EARA_QOS vpu_qos_counter_start(core); #endif return ret; } int vpu_shut_down(int core) { return vpu_shut_down_ex(core, 1); } int vpu_shut_down_ex(int core_s, int logbackup) { int ret = 0; unsigned int core = (unsigned int)core_s; #ifdef ENABLE_EARA_QOS vpu_qos_counter_end(core); if (core == 0) apu_power_count_enable(false, USER_VPU0); else if (core == 1) apu_power_count_enable(false, USER_VPU1); apu_shut_down(); #endif LOG_DBG("[vpu_%d] shutdown +\n", core); mutex_lock(&power_mutex[core]); if ((!is_power_on[core]) || (is_power_disable_off)) { LOG_ERR("[vpu_%d] shutdown abort pwr_on:%d,dis_off:%d)\n", core, is_power_on[core], is_power_disable_off); mutex_unlock(&power_mutex[core]); return 0; } mutex_lock(&(vpu_service_cores[core].state_mutex)); switch (vpu_service_cores[core].state) { case VCT_SHUTDOWN: case VCT_IDLE: case VCT_NONE: #ifdef MTK_VPU_FPGA_PORTING case VCT_BOOTUP: #endif vpu_service_cores[core].current_algo = 0; vpu_service_cores[core].state = VCT_SHUTDOWN; mutex_unlock(&(vpu_service_cores[core].state_mutex)); break; #ifndef MTK_VPU_FPGA_PORTING case VCT_BOOTUP: #endif case VCT_EXECUTING: case VCT_VCORE_CHG: mutex_unlock(&(vpu_service_cores[core].state_mutex)); goto out; /*break;*/ } #ifdef MET_POLLING_MODE if (g_func_mask & VFM_ROUTINE_PRT_SYSLOG) { ret = vpu_profile_state_set(core, 0); if (ret) { LOG_ERR("[vpu_%d] fail to profile_state_set 0\n", core); goto out; } } #endif if (logbackup) { vpu_service_cores[core].work_buf_logsize = vpu_read_field(core, FLD_XTENSA_INFO30); } vpu_trace_begin("%s", __func__); ret = vpu_disable_regulator_and_clock(core); if (ret) { LOG_ERR("[vpu_%d]fail to disable regulator and clock\n", core); goto out; } wake_up_interruptible(&waitq_change_vcore); out: vpu_trace_end(); mutex_unlock(&power_mutex[core]); LOG_DBG("[vpu_%d] shutdown -\n", core); #ifdef MTK_PERF_OBSERVER if (!ret) { struct pob_xpufreq_info pxi; pxi.id = core; pxi.opp = -1; pob_xpufreq_update(POB_XPUFREQ_VPU, &pxi); } #endif return ret; } int vpu_hw_load_algo(int core_s, struct vpu_algo *algo) { int ret; bool is_hw_fail = true; unsigned int core = (unsigned int)core_s; LOG_DBG("[vpu_%d] %s +\n", core, __func__); /* no need to reload algo if have same loaded algo*/ if (vpu_service_cores[core].current_algo == algo->id[core]) return 0; vpu_trace_begin("%s(%d)", __func__, algo->id[core]); ret = vpu_get_power(core, false); if (ret) { LOG_ERR("[vpu_%d]fail to get power!\n", core); goto out; } LOG_DBG("[vpu_%d] %s done\n", core, __func__); ret = wait_to_do_vpu_running(core); if (ret) { LOG_ERR("[vpu_%d] %s, ret=%d\n", core, "load_algo fail to wait_to_do_vpu_running!", ret); goto out; } mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_EXECUTING; mutex_unlock(&(vpu_service_cores[core].state_mutex)); lock_command(core, VPU_CMD_DO_LOADER); LOG_DBG("start to load algo\n"); ret = vpu_check_precond(core); if (ret) { LOG_ERR("[vpu_%d]have wrong status before do loader!\n", core); goto out; } LOG_DBG("[vpu_%d] vpu_check_precond done\n", core); LOG_DBG("[vpu_%d] algo ptr/length (0x%lx/0x%x)\n", core, (unsigned long)algo->bin_ptr, algo->bin_length); /* 1. write register */ vpu_write_field(core, FLD_XTENSA_INFO01, VPU_CMD_DO_LOADER); /* binary data's address */ vpu_write_field(core, FLD_XTENSA_INFO12, algo->bin_ptr); /* binary data's length */ vpu_write_field(core, FLD_XTENSA_INFO13, algo->bin_length); vpu_write_field(core, FLD_XTENSA_INFO15, opps.dsp.values[opps.dsp.index]); vpu_write_field(core, FLD_XTENSA_INFO16, opps.ipu_if.values[opps.ipu_if.index]); /* 2. trigger interrupt */ vpu_trace_begin("dsp:running"); LOG_DBG("[vpu_%d] dsp:running\n", core); /* RUN_STALL down */ vpu_write_field(core, FLD_RUN_STALL, 0); vpu_write_field(core, FLD_CTL_INT, 1); /* 3. wait until done */ ret = wait_command(core); if (ret == -ERESTARTSYS) is_hw_fail = false; LOG_DBG("[vpu_%d] algo done\n", core); /* RUN_STALL pull up to avoid fake cmd */ vpu_write_field(core, FLD_RUN_STALL, 1); vpu_trace_end(); if (ret) { vpu_err_hnd(is_hw_fail, core, NULL, "VPU Timeout", "DO_LOADER (%s) Timeout", algo->name); goto out; } /* 4. update the id of loaded algo */ vpu_service_cores[core].current_algo = algo->id[core]; out: unlock_command(core); if (ret) { mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_SHUTDOWN; mutex_unlock(&(vpu_service_cores[core].state_mutex)); /*vpu_put_power(core, VPT_IMT_OFF);*/ /* blocking use same ththread to avoid race between * delayedworkQ and serviceThread */ mutex_lock(&power_counter_mutex[core]); power_counter[core]--; mutex_unlock(&power_counter_mutex[core]); apu_get_power_info(); LOG_ERR("[vpu_%d] pr hw error, force shutdown, cnt(%d)\n", core, power_counter[core]); if (power_counter[core] == 0) vpu_shut_down(core); } else { vpu_put_power(core, VPT_ENQUE_ON); } vpu_trace_end(); LOG_DBG("[vpu] %s -\n", __func__); return ret; } /* do whole processing for enque request, including check algo, load algo, * run d2d. minimize timing gap between each step for a single eqneu request * and minimize the risk of timing issue */ int vpu_hw_processing_request(int core_s, struct vpu_request *request) { int ret; struct vpu_algo *algo = NULL; bool need_reload = false; struct timespec start, end; uint64_t latency = 0; bool is_hw_fail = true; unsigned int core = (unsigned int)core_s; mutex_lock(&vpu_dev->sdsp_control_mutex[core]); /* step1, enable clocks and boot-up if needed */ ret = vpu_get_power(core, false); if (ret) { LOG_ERR("[vpu_%d] fail to get power!\n", core); goto out2; } LOG_DBG("[vpu_%d] vpu_get_power done\n", core); /* step2. check algo */ if (request->algo_id[core] != vpu_service_cores[core].current_algo) { struct vpu_user *user = (struct vpu_user *)request->user_id; ret = vpu_find_algo_by_id(core, request->algo_id[core], &algo, user); need_reload = true; if (ret) { request->status = VPU_REQ_STATUS_INVALID; LOG_ERR("[vpu_%d] pr can not find the algo, id=%d\n", core, request->algo_id[core]); goto out2; } } LOG_INF("%s: vpu%d: algo: %s(%d)\n", __func__, core, algo ? algo->name : "", request->algo_id[core]); /* step3. do processing, algo loader and d2d*/ ret = wait_to_do_vpu_running(core); if (ret) { LOG_ERR("[vpu_%d] %s, ret=%d\n", core, "pr load_algo fail to wait_to_do_vpu_running!", ret); goto out; } mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_EXECUTING; mutex_unlock(&(vpu_service_cores[core].state_mutex)); /* algo loader if needed */ if (need_reload) { vpu_trace_begin("[vpu_%d] hw_load_algo(%d)", core, algo->id[core]); lock_command(core, VPU_CMD_DO_LOADER); LOG_DBG("start to load algo\n"); ret = vpu_check_precond(core); if (ret) { LOG_ERR("[vpu_%d]have wrong status before do loader!\n", core); goto out; } LOG_DBG("[vpu_%d] vpu_check_precond done\n", core); if (g_vpu_log_level > Log_ALGO_OPP_INFO) LOG_INF("[vpu_%d] algo_%d ptr/length (0x%lx/0x%x), bf(%d)\n", core, algo->id[core], (unsigned long)algo->bin_ptr, algo->bin_length, vpu_service_cores[core].is_cmd_done); /* 1. write register */ vpu_write_field(core, FLD_XTENSA_INFO01, VPU_CMD_DO_LOADER); /* binary data's address */ vpu_write_field(core, FLD_XTENSA_INFO12, algo->bin_ptr); /* binary data's length */ vpu_write_field(core, FLD_XTENSA_INFO13, algo->bin_length); vpu_write_field(core, FLD_XTENSA_INFO15, opps.dsp.values[opps.dsp.index]); vpu_write_field(core, FLD_XTENSA_INFO16, opps.ipu_if.values[opps.ipu_if.index]); /* 2. trigger interrupt */ vpu_trace_begin("[vpu_%d] dsp:load_algo running", core); LOG_DBG("[vpu_%d] dsp:load_algo running\n", core); /* RUN_STALL down */ vpu_write_field(core, FLD_RUN_STALL, 0); vpu_write_field(core, FLD_CTL_INT, 1); if (g_vpu_log_level > Log_STATE_MACHINE) { LOG_INF("[0x%lx]:load algo start ", (unsigned long)request->request_id); } /* 3. wait until done */ ret = wait_command(core); if (ret == -ERESTARTSYS) is_hw_fail = false; /* handle for err/exception isr */ if (exception_isr_check[core]) ret |= vpu_check_postcond(core); if (g_vpu_log_level > Log_ALGO_OPP_INFO) LOG_INF("[vpu_%d] algo_%d %s=0x%lx, %s=%d, %s=%d, %s=%d, %d\n", core, algo->id[core], "bin_ptr", (unsigned long)algo->bin_ptr, "done", vpu_service_cores[core].is_cmd_done, "ret", ret, "info00", vpu_read_field(core, FLD_XTENSA_INFO00), exception_isr_check[core]); if (ret) { exception_isr_check[core] = false; LOG_ERR("[vpu_%d] %s, status(%d/%d), ret(%d), %d\n", core, "pr load_algo err", vpu_read_field(core, FLD_XTENSA_INFO00), vpu_service_cores[core].is_cmd_done, ret, exception_isr_check[core]); } else { LOG_DBG("[vpu_%d] temp test1.2\n", core); /* RUN_STALL pull up to avoid fake cmd */ vpu_write_field(core, FLD_RUN_STALL, 1); } vpu_trace_end(); if (ret) { request->status = VPU_REQ_STATUS_TIMEOUT; vpu_err_hnd(is_hw_fail, core, request, "VPU Timeout", "vpu%d: DO_LOADER Timeout, algo: %s(%d)", core, algo->name, vpu_service_cores[core].current_algo); goto out; } /* 4. update the id of loaded algo */ vpu_service_cores[core].current_algo = algo->id[core]; unlock_command(core); vpu_trace_end(); } /* d2d operation */ vpu_trace_begin("[vpu_%d] hw_processing_request(%d)", core, request->algo_id[core]); LOG_DBG("start to enque request\n"); lock_command(core, VPU_CMD_DO_D2D); ret = vpu_check_precond(core); if (ret) { request->status = VPU_REQ_STATUS_BUSY; LOG_ERR("error state before enque request!\n"); goto out; } memcpy((void *) (uintptr_t)vpu_service_cores[core].work_buf->va, request->buffers, sizeof(struct vpu_buffer) * request->buffer_count); LOG_DBG("[vpu_%d]start d2d, %s(%d/%d), %s(%d), %s(%d/%d,%d), %s(%d)\n", core, "id/frm", request->algo_id[core], request->frame_magic, "bw", request->power_param.bw, "algo", vpu_service_cores[core].current_algo, request->algo_id[core], need_reload, "done", vpu_service_cores[core].is_cmd_done); /* 1. write register */ /* command: d2d */ vpu_write_field(core, FLD_XTENSA_INFO01, VPU_CMD_DO_D2D); /* buffer count */ vpu_write_field(core, FLD_XTENSA_INFO12, request->buffer_count); /* pointer to array of struct vpu_buffer */ vpu_write_field(core, FLD_XTENSA_INFO13, vpu_service_cores[core].work_buf->pa); /* pointer to property buffer */ vpu_write_field(core, FLD_XTENSA_INFO14, request->sett.sett_ptr); /* size of property buffer */ vpu_write_field(core, FLD_XTENSA_INFO15, request->sett.sett_lens); /* 2. trigger interrupt */ #ifdef ENABLE_PMQOS /* pmqos, 10880 Mbytes per second */ #endif vpu_trace_begin("[vpu_%d] dsp:d2d running", core); LOG_DBG("[vpu_%d] d2d running...\n", core); #if defined(VPU_MET_READY) MET_Events_Trace(1, core, request->algo_id[core]); #endif #ifdef ENABLE_EARA_QOS vpu_cmd_qos_start(core); #endif /* RUN_STALL pull down */ vpu_write_field(core, FLD_RUN_STALL, 0); vpu_write_field(core, FLD_CTL_INT, 1); ktime_get_ts(&start); if (g_vpu_log_level > Log_ALGO_OPP_INFO) LOG_INF("[0x%lx]:d2d start ", (unsigned long)request->request_id); /* 3. wait until done */ ret = wait_command(core); if (ret == -ERESTARTSYS) is_hw_fail = false; /* handle for err/exception isr */ if (exception_isr_check[core]) ret |= vpu_check_postcond(core); if (g_vpu_log_level > VpuLogThre_PERFORMANCE) { LOG_INF("[vpu_%d] end d2d, done(%d), ret(%d), info00(%d), %d\n", core, vpu_service_cores[core].is_cmd_done, ret, vpu_read_field(core, FLD_XTENSA_INFO00), exception_isr_check[core]); } if (ret) { exception_isr_check[core] = false; LOG_ERR("[vpu_%d] hw_d2d err, status(%d/%d), ret(%d), %d\n", core, vpu_read_field(core, FLD_XTENSA_INFO00), vpu_service_cores[core].is_cmd_done, ret, exception_isr_check[core]); } else { LOG_DBG("[vpu_%d] temp test2.2\n", core); /* RUN_STALL pull up to avoid fake cmd */ vpu_write_field(core, FLD_RUN_STALL, 1); } ktime_get_ts(&end); latency += (uint64_t)(timespec_to_ns(&end) - timespec_to_ns(&start)); if (g_vpu_log_level > Log_STATE_MACHINE) { LOG_INF("[0x%lx]:load algo + d2d latency[%lld] ", (unsigned long)request->request_id, latency); } #ifdef ENABLE_PMQOS /* pmqos, release request after d2d done */ #endif vpu_trace_end(); #if defined(VPU_MET_READY) MET_Events_Trace(0, core, request->algo_id[core]); #endif LOG_DBG("[vpu_%d] hw_d2d test , status(%d/%d), ret(%d)\n", core, vpu_read_field(core, FLD_XTENSA_INFO00), vpu_service_cores[core].is_cmd_done, ret); if (ret) { request->status = VPU_REQ_STATUS_TIMEOUT; vpu_err_hnd(is_hw_fail, core, request, "VPU Timeout", "vpu%d: D2D Timeout, algo: %s(%d)", core, algo ? algo->name : "", vpu_service_cores[core].current_algo); goto out; } request->status = (vpu_check_postcond(core)) ? VPU_REQ_STATUS_FAILURE : VPU_REQ_STATUS_SUCCESS; out: unlock_command(core); vpu_trace_end(); out2: if (ret) { mutex_lock(&(vpu_service_cores[core].state_mutex)); vpu_service_cores[core].state = VCT_SHUTDOWN; mutex_unlock(&(vpu_service_cores[core].state_mutex)); /* vpu_put_power(core, VPT_IMT_OFF);*/ /* blocking use same ththread to avoid race between * delayedworkQ and serviceThread */ mutex_lock(&power_counter_mutex[core]); power_counter[core]--; mutex_unlock(&power_counter_mutex[core]); apu_get_power_info(); LOG_ERR("[vpu_%d] pr hw error, force shutdown, cnt(%d)\n", core, power_counter[core]); if (power_counter[core] == 0) vpu_shut_down(core); } else { vpu_put_power(core, VPT_ENQUE_ON); } LOG_DBG("[vpu] %s - (%d)", __func__, request->status); request->busy_time = (uint64_t)latency; /*Todo*/ #ifdef ENABLE_EARA_QOS request->bandwidth = vpu_cmd_qos_end(core); #endif if (g_vpu_log_level > Log_STATE_MACHINE) { LOG_INF("[0x%lx]:vpu busy_time[%lld],bw[%d] ", (unsigned long)request->request_id, request->busy_time, request->bandwidth); } mutex_unlock(&vpu_dev->sdsp_control_mutex[core]); return ret; } int vpu_hw_get_algo_info(int core_s, struct vpu_algo *algo) { int ret = 0; int port_count = 0; int info_desc_count = 0; int sett_desc_count = 0; unsigned int ofs_ports, ofs_info, ofs_info_descs, ofs_sett_descs; int i; bool is_hw_fail = true; unsigned int core = (unsigned int)core_s; vpu_trace_begin("%s(%d)", __func__, algo->id[core]); ret = vpu_get_power(core, false); if (ret) { LOG_ERR("[vpu_%d]fail to get power!\n", core); goto out; } lock_command(core, VPU_CMD_GET_ALGO); LOG_DBG("start to get algo, algo_id=%d\n", algo->id[core]); ret = vpu_check_precond(core); if (ret) { LOG_ERR("[vpu_%d]have wrong status before get algo!\n", core); goto out; } ofs_ports = 0; ofs_info = sizeof(((struct vpu_algo *)0)->ports); ofs_info_descs = ofs_info + algo->info_length; ofs_sett_descs = ofs_info_descs + sizeof(((struct vpu_algo *)0)->info_descs); LOG_INF("[vpu_%d] %s check precond done\n", core, __func__); /* 1. write register */ vpu_write_field(core, FLD_XTENSA_INFO01, VPU_CMD_GET_ALGO); vpu_write_field(core, FLD_XTENSA_INFO06, vpu_service_cores[core].work_buf->pa + ofs_ports); vpu_write_field(core, FLD_XTENSA_INFO07, vpu_service_cores[core].work_buf->pa + ofs_info); vpu_write_field(core, FLD_XTENSA_INFO08, algo->info_length); vpu_write_field(core, FLD_XTENSA_INFO10, vpu_service_cores[core].work_buf->pa + ofs_info_descs); vpu_write_field(core, FLD_XTENSA_INFO12, vpu_service_cores[core].work_buf->pa + ofs_sett_descs); /* 2. trigger interrupt */ vpu_trace_begin("dsp:running"); LOG_DBG("[vpu] %s running...\n", __func__); /* RUN_STALL pull down */ vpu_write_field(core, FLD_RUN_STALL, 0); vpu_write_field(core, FLD_CTL_INT, 1); /* 3. wait until done */ ret = wait_command(core); if (ret == -ERESTARTSYS) is_hw_fail = false; LOG_INF("[vpu_%d] VPU_CMD_GET_ALGO done\n", core); vpu_trace_end(); if (ret) { vpu_err_hnd(is_hw_fail, core, NULL, "VPU Timeout", "vpu%d: GET_ALGO Timeout: %s(%d)", core, algo->name, algo->id[core]); goto out; } /* RUN_STALL pull up to avoid fake cmd */ vpu_write_field(core, FLD_RUN_STALL, 1); /* 4. get the return value */ port_count = vpu_read_field(core, FLD_XTENSA_INFO05); info_desc_count = vpu_read_field(core, FLD_XTENSA_INFO09); sett_desc_count = vpu_read_field(core, FLD_XTENSA_INFO11); algo->port_count = port_count; algo->info_desc_count = info_desc_count; algo->sett_desc_count = sett_desc_count; LOG_DBG("end of get algo, %s=%d, %s=%d, %s=%d\n", "port_count", port_count, "info_desc_count", info_desc_count, "sett_desc_count", sett_desc_count); /* 5. write back data from working buffer */ memcpy((void *)(uintptr_t)algo->ports, (void *)((uintptr_t)vpu_service_cores[core].work_buf->va + ofs_ports), sizeof(struct vpu_port) * port_count); for (i = 0 ; i < algo->port_count ; i++) { LOG_DBG("port %d.. id=%d, name=%s, dir=%d, usage=%d\n", i, algo->ports[i].id, algo->ports[i].name, algo->ports[i].dir, algo->ports[i].usage); } memcpy((void *)(uintptr_t)algo->info_ptr, (void *)((uintptr_t)vpu_service_cores[core].work_buf->va + ofs_info), algo->info_length); memcpy((void *)(uintptr_t)algo->info_descs, (void *)((uintptr_t)vpu_service_cores[core].work_buf->va + ofs_info_descs), sizeof(struct vpu_prop_desc) * info_desc_count); memcpy((void *)(uintptr_t)algo->sett_descs, (void *)((uintptr_t)vpu_service_cores[core].work_buf->va + ofs_sett_descs), sizeof(struct vpu_prop_desc) * sett_desc_count); LOG_DBG("end of get algo 2, %s=%d, %s=%d, %s=%d\n", "port_count", algo->port_count, "info_desc_count", algo->info_desc_count, "sett_desc_count", algo->sett_desc_count); out: unlock_command(core); if (ret) apu_get_power_info(); vpu_put_power(core, VPT_ENQUE_ON); vpu_trace_end(); LOG_DBG("[vpu] %s -\n", __func__); return ret; } void vpu_hw_lock(struct vpu_user *user) { /* CHRISTODO */ int TEMP_CORE = 0; if (user->locked) LOG_ERR("double locking bug, pid=%d, tid=%d\n", user->open_pid, user->open_tgid); else { mutex_lock(&lock_mutex); is_locked = true; user->locked = true; vpu_get_power(TEMP_CORE, false); } } void vpu_hw_unlock(struct vpu_user *user) { /* CHRISTODO */ int TEMP_CORE = 0; if (user->locked) { vpu_put_power(TEMP_CORE, VPT_ENQUE_ON); is_locked = false; user->locked = false; wake_up_interruptible(&lock_wait); mutex_unlock(&lock_mutex); } else LOG_ERR("should not unlock while unlocked, pid=%d, tid=%d\n", user->open_pid, user->open_tgid); } int vpu_alloc_shared_memory(struct vpu_shared_memory **shmem, struct vpu_shared_memory_param *param) { int ret = 0; /* CHRISTODO */ struct ion_mm_data mm_data; struct ion_sys_data sys_data; struct ion_handle *handle = NULL; *shmem = kzalloc(sizeof(struct vpu_shared_memory), GFP_KERNEL); ret = (*shmem == NULL); if (ret) { LOG_ERR("fail to kzalloc 'struct memory'!\n"); goto out; } handle = ion_alloc(ion_client, param->size, 0, ION_HEAP_MULTIMEDIA_MASK, 0); ret = (handle == NULL) ? -ENOMEM : 0; if (ret) { LOG_ERR("fail to alloc ion buffer, ret=%d\n", ret); goto out; } (*shmem)->handle = (void *) handle; mm_data.mm_cmd = ION_MM_CONFIG_BUFFER_EXT; mm_data.config_buffer_param.kernel_handle = handle; mm_data.config_buffer_param.module_id = VPU_PORT_OF_IOMMU; mm_data.config_buffer_param.security = 0; mm_data.config_buffer_param.coherent = 1; if (param->fixed_addr) { mm_data.config_buffer_param.reserve_iova_start = param->fixed_addr; mm_data.config_buffer_param.reserve_iova_end = IOMMU_VA_END; } else { /* CHRISTODO, need revise starting address for working buffer*/ mm_data.config_buffer_param.reserve_iova_start = 0x60000000; mm_data.config_buffer_param.reserve_iova_end = IOMMU_VA_END; } ret = ion_kernel_ioctl(ion_client, ION_CMD_MULTIMEDIA, (unsigned long)&mm_data); if (ret) { LOG_ERR("fail to config ion buffer, ret=%d\n", ret); goto out; } /* map pa */ LOG_DBG("vpu param->require_pa(%d)\n", param->require_pa); if (param->require_pa) { sys_data.sys_cmd = ION_SYS_GET_PHYS; sys_data.get_phys_param.kernel_handle = handle; sys_data.get_phys_param.phy_addr = (unsigned long)(VPU_PORT_OF_IOMMU) << 24 | ION_FLAG_GET_FIXED_PHYS; sys_data.get_phys_param.len = ION_FLAG_GET_FIXED_PHYS; ret = ion_kernel_ioctl(ion_client, ION_CMD_SYSTEM, (unsigned long)&sys_data); if (ret) { LOG_ERR("fail to get ion phys, ret=%d\n", ret); goto out; } (*shmem)->pa = sys_data.get_phys_param.phy_addr; (*shmem)->length = sys_data.get_phys_param.len; } /* map va */ if (param->require_va) { (*shmem)->va = (uint64_t)(uintptr_t)ion_map_kernel(ion_client, handle); ret = ((*shmem)->va) ? 0 : -ENOMEM; if (ret) { LOG_ERR("fail to map va of buffer!\n"); goto out; } } return 0; out: if (handle) ion_free(ion_client, handle); if (*shmem) { kfree(*shmem); *shmem = NULL; } return ret; } void vpu_free_shared_memory(struct vpu_shared_memory *shmem) { struct ion_handle *handle; if (shmem == NULL) return; handle = (struct ion_handle *) shmem->handle; if (handle) { ion_unmap_kernel(ion_client, handle); ion_free(ion_client, handle); } kfree(shmem); } int vpu_dump_vpu_memory(struct seq_file *s) { vpu_dmp_seq(s); vpu_dump_image_file(s); return 0; } int vpu_dump_register(struct seq_file *s) { return 0; } int vpu_dump_image_file(struct seq_file *s) { int i, j, id = 1; struct vpu_algo_info *algo_info; struct vpu_image_header *header; int core = 0; #define LINE_BAR " +------+-----+--------------------------------+--------\n" vpu_print_seq(s, LINE_BAR); vpu_print_seq(s, " |%-6s|%-5s|%-32s|%-8s|%-11s|%-10s|\n", "Header", "Id", "Name", "MagicNum", "MVA", "Length"); vpu_print_seq(s, LINE_BAR); header = (struct vpu_image_header *) ((uintptr_t)vpu_service_cores[core].bin_base + (VPU_OFFSET_IMAGE_HEADERS)); for (i = 0; i < VPU_NUMS_IMAGE_HEADER; i++) { for (j = 0; j < header[i].algo_info_count; j++) { algo_info = &header[i].algo_infos[j]; vpu_print_seq(s, " |%-6d|%-5d|%-32s|0x%-6lx|0x%-9lx|0x%-8x|\n", (i + 1), id, algo_info->name, (unsigned long)(algo_info->vpu_core), algo_info->offset - VPU_OFFSET_ALGO_AREA + (uintptr_t)vpu_service_cores[core].algo_data_mva, algo_info->length); id++; } } vpu_print_seq(s, LINE_BAR); #undef LINE_BAR return 0; } int vpu_dump_mesg(struct seq_file *s) { vpu_dump_mesg_seq(s, 0); vpu_dump_mesg_seq(s, 1); return 0; } int vpu_dump_mesg_seq(struct seq_file *s, int core_s) { char *ptr = NULL; char *log_head = NULL; char *log_buf; char *log_a_pos = NULL; bool jump_out = false; unsigned int core = (unsigned int)core_s; log_buf = (char *) ((uintptr_t)vpu_service_cores[core].work_buf->va + VPU_OFFSET_LOG); ptr = log_buf; log_head = log_buf; /* set the last byte to '\0' */ vpu_print_seq(s, "=== VPU_%d Log Buffer ===\n", core); vpu_print_seq(s, "vpu: print dsp log (0x%x):\n", (unsigned int)(uintptr_t)log_buf); /* in case total log < VPU_SIZE_LOG_SHIFT and there's '\0' */ *(log_head + VPU_SIZE_LOG_BUF - 1) = '\0'; vpu_print_seq(s, "%s", ptr+VPU_SIZE_LOG_HEADER); ptr += VPU_SIZE_LOG_HEADER; log_head = ptr; jump_out = false; *(log_head + VPU_SIZE_LOG_DATA - 1) = '\n'; do { if ((ptr + VPU_SIZE_LOG_SHIFT) >= (log_head + VPU_SIZE_LOG_DATA)) { /* last part of log buffer */ *(log_head + VPU_SIZE_LOG_DATA - 1) = '\0'; jump_out = true; } else { log_a_pos = strchr(ptr + VPU_SIZE_LOG_SHIFT, '\n'); if (log_a_pos == NULL) break; *log_a_pos = '\0'; } vpu_print_seq(s, "%s\n", ptr); ptr = log_a_pos + 1; /* incase log_a_pos is at end of string */ if (ptr >= log_head + VPU_SIZE_LOG_DATA) break; } while (!jump_out); if (vpu_service_cores[core].work_buf && s) { seq_printf(s, "\n======== vpu%d: logbuf @0x%x ========\n", core, vpu_service_cores[core].work_buf->pa + VPU_OFFSET_LOG); seq_hex_dump(s, "logbuf ", DUMP_PREFIX_OFFSET, 32, 4, (void *)(vpu_service_cores[core].work_buf->va + VPU_OFFSET_LOG), VPU_SIZE_LOG_BUF, true); } else pr_info("%s: NULL handle for dump!\n", __func__); return 0; } int vpu_dump_opp_table(struct seq_file *s) { int i; #define LINE_BAR " +-----+----------+----------+------------+-----------+-----------+\n" vpu_print_seq(s, LINE_BAR); vpu_print_seq(s, " |%-5s|%-10s|%-10s|%-10s|%-12s|%-11s|%-11s|\n", "OPP", "VCORE(uV)", "VVPU(uV)", "DSP(KHz)", "IPU_IF(KHz)", "DSP1(KHz)", "DSP2(KHz)"); vpu_print_seq(s, LINE_BAR); for (i = 0; i < opps.count; i++) { vpu_print_seq(s, " |%-5d|[%d]%-7d|[%d]%-7d|[%d]%-7d|[%d]%-9d|[%d]%-8d|[%d]%-8d|\n", i, opps.vcore.opp_map[i], opps.vcore.values[opps.vcore.opp_map[i]], opps.vvpu.opp_map[i], opps.vvpu.values[opps.vvpu.opp_map[i]], opps.dsp.opp_map[i], opps.dsp.values[opps.dsp.opp_map[i]], opps.ipu_if.opp_map[i], opps.ipu_if.values[opps.ipu_if.opp_map[i]], opps.dspcore[0].opp_map[i], opps.dspcore[0].values[opps.dspcore[0].opp_map[i]], opps.dspcore[1].opp_map[i], opps.dspcore[1].values[opps.dspcore[1].opp_map[i]]); } vpu_print_seq(s, LINE_BAR); #undef LINE_BAR return 0; } int vpu_dump_power(struct seq_file *s) { int vvpu_opp = 0; vvpu_opp = vpu_get_hw_vvpu_opp(0); vpu_print_seq(s, "%s(rw): %s[%d/%d]\n", "dvfs_debug", "vvpu", opps.vvpu.index, vvpu_opp); vpu_print_seq(s, "%s[%d], %s[%d], %s[%d], %s[%d]\n", "dsp", opps.dsp.index, "ipu_if", opps.ipu_if.index, "dsp1", opps.dspcore[0].index, "dsp2", opps.dspcore[1].index); vpu_print_seq(s, "min/max boost[0][%d/%d], min/max opp[1][%d/%d]\n", min_boost[0], max_boost[0], min_boost[1], max_boost[1]); vpu_print_seq(s, "min/max opp[0][%d/%d], min/max opp[1][%d/%d]\n", min_opp[0], max_opp[0], min_opp[1], max_opp[1]); vpu_print_seq(s, "is_power_debug_lock(rw): %d\n", is_power_debug_lock); return 0; } int vpu_dump_vpu(struct seq_file *s) { int core; #define LINE_BAR " +-------------+------+-------+-------+-------+-------+\n" vpu_print_seq(s, LINE_BAR); vpu_print_seq(s, " |%-12s|%-34s|\n", "Queue#", "Waiting"); vpu_print_seq(s, LINE_BAR); vpu_print_seq(s, " |%-12s|%-34d|\n", "Common", vpu_pool_size(&vpu_dev->pool_common)); for (core = 0 ; core < MTK_VPU_CORE; core++) { vpu_print_seq(s, " |Core %-7d|%-34d|\n", core, vpu_pool_size(&vpu_dev->pool[core])); } vpu_print_seq(s, "\n"); #undef LINE_BAR return 0; } int vpu_set_power_parameter(uint8_t param, int argc, int *args) { int ret = 0; unsigned int lv = 0; switch (param) { case VPU_POWER_PARAM_FIX_OPP: ret = (argc == 1) ? 0 : -EINVAL; if (ret) { LOG_ERR("invalid argument, expected:1, received:%d\n", argc); goto out; } switch (args[0]) { case 0: is_power_debug_lock = false; break; case 1: is_power_debug_lock = true; break; default: if (ret) { LOG_ERR("invalid argument, received:%d\n", (int)(args[0])); goto out; } ret = -EINVAL; goto out; } break; case VPU_POWER_PARAM_DVFS_DEBUG: ret = (argc == 1) ? 0 : -EINVAL; if (ret) { LOG_ERR("invalid argument, expected:1, received:%d\n", argc); goto out; } lv = (unsigned int)args[0]; ret = lv >= opps.count; if (ret) { LOG_ERR("opp step(%d) is out-of-bound, count:%d\n", (int)(args[0]), opps.count); goto out; } opps.vcore.index = opps.vcore.opp_map[lv]; opps.vvpu.index = opps.vvpu.opp_map[lv]; opps.dsp.index = opps.dsp.opp_map[lv]; opps.ipu_if.index = opps.ipu_if.opp_map[lv]; opps.dspcore[0].index = opps.dspcore[0].opp_map[lv]; opps.dspcore[1].index = opps.dspcore[1].opp_map[lv]; is_power_debug_lock = true; break; case VPU_POWER_PARAM_JTAG: ret = (argc == 1) ? 0 : -EINVAL; if (ret) { LOG_ERR("invalid argument, expected:1, received:%d\n", argc); goto out; } is_jtag_enabled = args[0]; ret = vpu_hw_enable_jtag(is_jtag_enabled); break; case VPU_POWER_PARAM_LOCK: ret = (argc == 1) ? 0 : -EINVAL; if (ret) { LOG_ERR("invalid argument, expected:1, received:%d\n", argc); goto out; } is_power_debug_lock = args[0]; break; case VPU_POWER_HAL_CTL: { struct vpu_lock_power vpu_lock_power; ret = (argc == 3) ? 0 : -EINVAL; if (ret) { LOG_ERR("invalid argument, expected:3, received:%d\n", argc); goto out; } if (args[0] > MTK_VPU_CORE || args[0] < 1) { LOG_ERR("core(%d) is out-of-bound\n", (int)(args[0])); goto out; } if (args[1] > 100 || args[1] < 0) { LOG_ERR("min boost(%d) is out-of-bound\n", (int)(args[1])); goto out; } if (args[2] > 100 || args[2] < 0) { LOG_ERR("max boost(%d) is out-of-bound\n", (int)(args[2])); goto out; } vpu_lock_power.core = args[0]; vpu_lock_power.lock = true; vpu_lock_power.priority = POWER_HAL; vpu_lock_power.max_boost_value = args[2]; vpu_lock_power.min_boost_value = args[1]; LOG_INF("[vpu]POWER_HAL_LOCK+core:%d, maxb:%d, minb:%d\n", vpu_lock_power.core, vpu_lock_power.max_boost_value, vpu_lock_power.min_boost_value); ret = vpu_lock_set_power(&vpu_lock_power); if (ret) { LOG_ERR("[POWER_HAL_LOCK]failed, ret=%d\n", ret); goto out; } break; } case VPU_EARA_CTL: { struct vpu_lock_power vpu_lock_power; ret = (argc == 3) ? 0 : -EINVAL; if (ret) { LOG_ERR("invalid argument, expected:3, received:%d\n", argc); goto out; } if (args[0] > MTK_VPU_CORE || args[0] < 1) { LOG_ERR("core(%d) is out-of-bound\n", (int)(args[0])); goto out; } if (args[1] > 100 || args[1] < 0) { LOG_ERR("min boost(%d) is out-of-bound\n", (int)(args[1])); goto out; } if (args[2] > 100 || args[2] < 0) { LOG_ERR("max boost(%d) is out-of-bound\n", (int)(args[2])); goto out; } vpu_lock_power.core = args[0]; vpu_lock_power.lock = true; vpu_lock_power.priority = EARA_QOS; vpu_lock_power.max_boost_value = args[2]; vpu_lock_power.min_boost_value = args[1]; LOG_INF("[vpu]EARA_LOCK+core:%d, maxb:%d, minb:%d\n", vpu_lock_power.core, vpu_lock_power.max_boost_value, vpu_lock_power.min_boost_value); ret = vpu_lock_set_power(&vpu_lock_power); if (ret) { LOG_ERR("[POWER_HAL_LOCK]failed, ret=%d\n", ret); goto out; } break; } case VPU_CT_INFO: ret = (argc == 1) ? 0 : -EINVAL; if (ret) { LOG_ERR("invalid argument, expected:1, received:%d\n", argc); goto out; } apu_dvfs_dump_info(); break; case VPU_POWER_PARAM_DISABLE_OFF: ret = (argc == 1) ? 0 : -EINVAL; if (ret) { LOG_ERR("invalid argument, expected:1, received:%d\n", argc); goto out; } is_power_disable_off = args[0]; break; default: LOG_ERR("unsupport the power parameter:%d\n", param); break; } out: return ret; } uint8_t vpu_boost_value_to_opp(uint8_t boost_value) { int ret = 0; /* set dsp frequency - 0:750 MHz, 1:700 MHz, 2:624 MHz, 3:594 MHz*/ /* set dsp frequency - 4:560 MHz, 5:525 MHz, 6:450 MHz, 7:416 MHz*/ /* set dsp frequency - 8:364 MHz, 9:312 MHz, 10:273 MH, 11:208 MH*/ /* set dsp frequency - 12:137 MHz, 13:104 MHz, 14:52 MHz, 15:26 MHz*/ uint32_t freq = 0; uint32_t freq0 = opps.dspcore[0].values[0]; uint32_t freq1 = opps.dspcore[0].values[1]; uint32_t freq2 = opps.dspcore[0].values[2]; uint32_t freq3 = opps.dspcore[0].values[3]; uint32_t freq4 = opps.dspcore[0].values[4]; uint32_t freq5 = opps.dspcore[0].values[5]; uint32_t freq6 = opps.dspcore[0].values[6]; uint32_t freq7 = opps.dspcore[0].values[7]; uint32_t freq8 = opps.dspcore[0].values[8]; uint32_t freq9 = opps.dspcore[0].values[9]; uint32_t freq10 = opps.dspcore[0].values[10]; uint32_t freq11 = opps.dspcore[0].values[11]; uint32_t freq12 = opps.dspcore[0].values[12]; uint32_t freq13 = opps.dspcore[0].values[13]; uint32_t freq14 = opps.dspcore[0].values[14]; uint32_t freq15 = opps.dspcore[0].values[15]; if (boost_value <= 100) freq = boost_value * freq0 / 100; else freq = freq0; if (freq <= freq0 && freq > freq1) ret = 0; else if (freq <= freq1 && freq > freq2) ret = 1; else if (freq <= freq2 && freq > freq3) ret = 2; else if (freq <= freq3 && freq > freq4) ret = 3; else if (freq <= freq4 && freq > freq5) ret = 4; else if (freq <= freq5 && freq > freq6) ret = 5; else if (freq <= freq6 && freq > freq7) ret = 6; else if (freq <= freq7 && freq > freq8) ret = 7; else if (freq <= freq8 && freq > freq9) ret = 8; else if (freq <= freq9 && freq > freq10) ret = 9; else if (freq <= freq10 && freq > freq11) ret = 10; else if (freq <= freq11 && freq > freq12) ret = 11; else if (freq <= freq12 && freq > freq13) ret = 12; else if (freq <= freq13 && freq > freq14) ret = 13; else if (freq <= freq14 && freq > freq15) ret = 14; else ret = 15; LOG_DVFS("%s opp %d\n", __func__, ret); return ret; } bool vpu_update_lock_power_parameter(struct vpu_lock_power *vpu_lock_power) { bool ret = true; unsigned int i, core = 0xFFFFFFFF; unsigned int priority = vpu_lock_power->priority; for (i = 0 ; i < MTK_VPU_CORE ; i++) { if (vpu_lock_power->core == (0x1 << i)) { core = i; break; } } if (core >= MTK_VPU_CORE) { LOG_ERR("wrong core index (0x%x/%d/%d)", vpu_lock_power->core, core, MTK_VPU_CORE); ret = false; return ret; } lock_power[priority][core].core = core; lock_power[priority][core].max_boost_value = vpu_lock_power->max_boost_value; lock_power[priority][core].min_boost_value = vpu_lock_power->min_boost_value; lock_power[priority][core].lock = true; lock_power[priority][core].priority = vpu_lock_power->priority; LOG_INF("power_parameter core %d, maxb:%d, minb:%d priority %d\n", lock_power[priority][core].core, lock_power[priority][core].max_boost_value, lock_power[priority][core].min_boost_value, lock_power[priority][core].priority); return ret; } bool vpu_update_unlock_power_parameter(struct vpu_lock_power *vpu_lock_power) { bool ret = true; unsigned int i, core = 0xFFFFFFFF; unsigned int priority = vpu_lock_power->priority; for (i = 0 ; i < MTK_VPU_CORE ; i++) { if (vpu_lock_power->core == (0x1 << i)) { core = i; break; } } if (core >= MTK_VPU_CORE) { LOG_ERR("wrong core index (0x%x/%d/%d)", vpu_lock_power->core, core, MTK_VPU_CORE); ret = false; return ret; } lock_power[priority][core].core = vpu_lock_power->core; lock_power[priority][core].max_boost_value = vpu_lock_power->max_boost_value; lock_power[priority][core].min_boost_value = vpu_lock_power->min_boost_value; lock_power[priority][core].lock = false; lock_power[priority][core].priority = vpu_lock_power->priority; LOG_INF("%s\n", __func__); return ret; } uint8_t min_of(uint8_t value1, uint8_t value2) { if (value1 <= value2) return value1; else return value2; } uint8_t max_of(uint8_t value1, uint8_t value2) { if (value1 <= value2) return value2; else return value1; } bool vpu_update_max_opp(struct vpu_lock_power *vpu_lock_power) { bool ret = true; unsigned int i, core = 0xFFFFFFFF; uint8_t first_priority = NORMAL; uint8_t first_priority_max_boost_value = 100; uint8_t first_priority_min_boost_value = 0; uint8_t temp_max_boost_value = 100; uint8_t temp_min_boost_value = 0; bool lock = false; uint8_t priority = NORMAL; uint8_t maxboost = 100; uint8_t minboost = 0; for (i = 0 ; i < MTK_VPU_CORE ; i++) { if (vpu_lock_power->core == (0x1 << i)) { core = i; break; } } if (core >= MTK_VPU_CORE) { LOG_ERR("wrong core index (0x%x/%d/%d)", vpu_lock_power->core, core, MTK_VPU_CORE); ret = false; return ret; } for (i = 0 ; i < VPU_OPP_PRIORIYY_NUM ; i++) { if (lock_power[i][core].lock == true && lock_power[i][core].priority < NORMAL) { first_priority = i; first_priority_max_boost_value = lock_power[i][core].max_boost_value; first_priority_min_boost_value = lock_power[i][core].min_boost_value; break; } } temp_max_boost_value = first_priority_max_boost_value; temp_min_boost_value = first_priority_min_boost_value; /*find final_max_boost_value*/ for (i = first_priority ; i < VPU_OPP_PRIORIYY_NUM ; i++) { lock = lock_power[i][core].lock; priority = lock_power[i][core].priority; maxboost = lock_power[i][core].max_boost_value; minboost = lock_power[i][core].min_boost_value; if (lock == true && priority < NORMAL && (((maxboost <= temp_max_boost_value) && (maxboost >= temp_min_boost_value)) || ((minboost <= temp_max_boost_value) && (minboost >= temp_min_boost_value)) || ((maxboost >= temp_max_boost_value) && (minboost <= temp_min_boost_value)))) { temp_max_boost_value = min_of(temp_max_boost_value, lock_power[i][core].max_boost_value); temp_min_boost_value = max_of(temp_min_boost_value, lock_power[i][core].min_boost_value); } } max_boost[core] = temp_max_boost_value; min_boost[core] = temp_min_boost_value; max_opp[core] = vpu_boost_value_to_opp(temp_max_boost_value); min_opp[core] = vpu_boost_value_to_opp(temp_min_boost_value); LOG_DVFS("final_min_boost_value:%d final_max_boost_value:%d\n", temp_min_boost_value, temp_max_boost_value); return ret; } int vpu_lock_set_power(struct vpu_lock_power *vpu_lock_power) { int ret = -1; unsigned int i, core = 0xFFFFFFFF; mutex_lock(&power_lock_mutex); for (i = 0 ; i < MTK_VPU_CORE ; i++) { if (vpu_lock_power->core == (0x1 << i)) { core = i; break; } } if (core >= MTK_VPU_CORE) { LOG_ERR("wrong core index (0x%x/%d/%d)", vpu_lock_power->core, core, MTK_VPU_CORE); ret = -1; mutex_unlock(&power_lock_mutex); return ret; } if (!vpu_update_lock_power_parameter(vpu_lock_power)) { mutex_unlock(&power_lock_mutex); return -1; } if (!vpu_update_max_opp(vpu_lock_power)) { mutex_unlock(&power_lock_mutex); return -1; } mutex_unlock(&power_lock_mutex); return 0; } int vpu_unlock_set_power(struct vpu_lock_power *vpu_lock_power) { int ret = -1; unsigned int i, core = 0xFFFFFFFF; mutex_lock(&power_lock_mutex); for (i = 0 ; i < MTK_VPU_CORE ; i++) { if (vpu_lock_power->core == (0x1 << i)) { core = i; break; } } if (core >= MTK_VPU_CORE) { LOG_ERR("wrong core index (0x%x/%d/%d)", vpu_lock_power->core, core, MTK_VPU_CORE); ret = false; mutex_unlock(&power_lock_mutex); return ret; } if (!vpu_update_unlock_power_parameter(vpu_lock_power)) { mutex_unlock(&power_lock_mutex); return -1; } if (!vpu_update_max_opp(vpu_lock_power)) { mutex_unlock(&power_lock_mutex); return -1; } mutex_unlock(&power_lock_mutex); return ret; } void vpu_lock(int core) { mutex_lock(&vpu_dev->sdsp_control_mutex[core]); } void vpu_unlock(int core) { mutex_unlock(&vpu_dev->sdsp_control_mutex[core]); } uint32_t vpu_get_iram_data(int core) { if (core < 0 || core >= MTK_VPU_CORE) return 0; return vpu_service_cores[core].iram_data_mva; } struct vpu_shared_memory *vpu_get_kernel_lib(int core) { if (core < 0 || core >= MTK_VPU_CORE) return NULL; if (!vpu_service_cores[core].exec_kernel_lib) return NULL; return vpu_service_cores[core].exec_kernel_lib; } struct vpu_shared_memory *vpu_get_work_buf(int core) { if (core < 0 || core >= MTK_VPU_CORE) return NULL; if (!vpu_service_cores[core].work_buf) return NULL; return vpu_service_cores[core].work_buf; } unsigned long vpu_get_ctrl_base(int core) { if (core < 0 || core >= MTK_VPU_CORE) return 0; return vpu_service_cores[core].vpu_base; }