/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include "mtkfb_info.h" #include "mtkfb.h" #include "ddp_hal.h" #include "ddp_dump.h" #include "ddp_path.h" #include "ddp_drv.h" #include "ddp_info.h" #include #include "cmdq_def.h" #include "cmdq_record.h" #include "cmdq_reg.h" #include "cmdq_core.h" #include "ddp_manager.h" #include "ddp_mmp.h" #include "ddp_ovl.h" #include "ddp_reg.h" #include "ddp_clkmgr.h" #include "lcm_drv.h" #include "extd_platform.h" #include "extd_log.h" #include "extd_utils.h" #include "extd_hdmi_types.h" #include "external_display.h" #include "disp_session.h" #include "disp_lowpower.h" #include "disp_recovery.h" #include "display_recorder.h" #include "extd_info.h" #include "mtkfb_fence.h" #include "disp_drv_log.h" #if (defined CONFIG_MTK_HDMI_SUPPORT) #else unsigned int dst_is_dsi; #endif int ext_disp_use_cmdq; int ext_disp_use_m4u; enum EXT_DISP_PATH_MODE ext_disp_mode; static struct disp_lcm_handle *plcm_interface; static int is_context_inited; static int init_roi; static unsigned int gCurrentPresentFenceIndex = -1; static struct mutex esd_check_lock; struct ext_disp_path_context { enum EXTD_POWER_STATE state; enum EXTD_OVL_REQ_STATUS ovl_req_state; enum EXTD_LCM_STATE lcm_state; int init; unsigned int session; int need_trigger_overlay; int suspend_config; enum EXT_DISP_PATH_MODE mode; unsigned int last_vsync_tick; struct mutex lock; char *mutex_locker; struct disp_lcm_handle *plcm; struct cmdqRecStruct *cmdq_handle_config; struct cmdqRecStruct *cmdq_handle_trigger; disp_path_handle dpmgr_handle; disp_path_handle ovl2mem_path_handle; cmdqBackupSlotHandle ext_cur_config_fence; cmdqBackupSlotHandle ext_subtractor_when_free; /* **cmdqBackupSlotHandle ext_input_config_info **bit0-7: layer_type **bit8: 0 = source is ovl, 1 = source is rdma */ cmdqBackupSlotHandle ext_input_config_info; #ifdef EXTD_DEBUG_SUPPORT cmdqBackupSlotHandle ext_ovl_rdma_status_info; #endif }; #define pgc _get_context() struct LCM_PARAMS extd_lcm_params; atomic_t g_extd_trigger_ticket = ATOMIC_INIT(1); atomic_t g_extd_release_ticket = ATOMIC_INIT(1); unsigned int g_extd_mobilelog; static struct ext_disp_path_context *_get_context(void) { static struct ext_disp_path_context g_context; if (!is_context_inited) { memset((void *)&g_context, 0, sizeof(struct ext_disp_path_context)); is_context_inited = 1; EXTDMSG("%s set is_context_inited\n", __func__); } return &g_context; } /************************** Upper Layer To HAL*********************************/ static struct EXTERNAL_DISPLAY_UTIL_FUNCS external_display_util = { 0 }; void extd_disp_drv_set_util_funcs(const struct EXTERNAL_DISPLAY_UTIL_FUNCS *util) { memcpy(&external_display_util, util, sizeof(struct EXTERNAL_DISPLAY_UTIL_FUNCS)); } enum EXT_DISP_PATH_MODE ext_disp_path_get_mode(unsigned int session) { return ext_disp_mode; } void ext_disp_path_set_mode(enum EXT_DISP_PATH_MODE mode, unsigned int session) { ext_disp_mode = EXTD_DIRECT_LINK_MODE; /*mode; */ init_roi = 1; } static void _ext_disp_path_lock(const char *caller) { extd_sw_mutex_lock(&(pgc->lock)); pgc->mutex_locker = (char *)caller; /* EXTDINFO("_ext_disp_path_lock caller: %s\n", pgc->mutex_locker);*/ } static void _ext_disp_path_unlock(const char *caller) { pgc->mutex_locker = NULL; extd_sw_mutex_unlock(&(pgc->lock)); /* EXTDINFO("_ext_disp_path_unlock caller: %s\n", pgc->mutex_locker);*/ } int ext_disp_manual_lock(void) { _ext_disp_path_lock(__func__); return 0; } int ext_disp_manual_unlock(void) { _ext_disp_path_unlock(__func__); return 0; } /* * trigger operation: VDO+CMDQ CMD+CMDQ VDO+CPU CMD+CPU * 1.wait idle: N N Y Y * 2.lcm update: N Y N Y * 3.path start: idle->Y Y idle->Y Y * 4.path trigger: idle->Y Y idle->Y Y * 5.mutex enable: N N idle->Y Y * 6.set cmdq dirty: N Y N N * 7.flush cmdq: Y Y N N * 8.reset cmdq: Y Y N N * 9.cmdq insert token: Y Y N N */ /* * trigger operation: VDO+CMDQ CMD+CMDQ VDO+CPU CMD+CPU * 1.wait idle: N N Y Y */ static int _should_wait_path_idle(void) { if (ext_disp_cmdq_enabled()) return 0; else return dpmgr_path_is_busy(pgc->dpmgr_handle); } /* trigger operation: VDO+CMDQ CMD+CMDQ VDO+CPU CMD+CPU * 3.path start: idle->Y Y idle->Y Y */ static int _should_start_path(void) { if (ext_disp_is_video_mode()) return dpmgr_path_is_idle(pgc->dpmgr_handle); else return 1; } /* trigger operation: VDO+CMDQ CMD+CMDQ VDO+CPU CMD+CPU * 4. path trigger: idle->Y Y idle->Y Y * 5. mutex enable: N N idle->Y Y */ static int _should_trigger_path(void) { if (ext_disp_is_video_mode()) return dpmgr_path_is_idle(pgc->dpmgr_handle); else if (ext_disp_cmdq_enabled()) return 0; else return 1; } /* trigger operation: VDO+CMDQ CMD+CMDQ VDO+CPU CMD+CPU * 6. set cmdq dirty: N Y N N */ static int _should_set_cmdq_dirty(void) { if (ext_disp_cmdq_enabled() && (ext_disp_is_video_mode() == 0)) return 1; else return 0; } /* trigger operation: VDO+CMDQ CMD+CMDQ VDO+CPU CMD+CPU * 7. flush cmdq: Y Y N N */ static int _should_flush_cmdq_config_handle(void) { return ext_disp_cmdq_enabled() ? 1 : 0; } /* trigger operation: VDO+CMDQ CMD+CMDQ VDO+CPU CMD+CPU * 8. reset cmdq: Y Y N N */ static int _should_reset_cmdq_config_handle(void) { return ext_disp_cmdq_enabled() ? 1 : 0; } /* trigger operation: VDO+CMDQ CMD+CMDQ VDO+CPU CMD+CPU * 9. cmdq insert token: Y Y N N */ static int _should_insert_wait_frame_done_token(void) { return ext_disp_cmdq_enabled() ? 1 : 0; } /* * static int _should_trigger_interface(void) * { * if (pgc->mode == EXTD_DECOUPLE_MODE) * return 0; * else * return 1; * } */ static int _should_config_ovl_input(void) { if (ext_disp_mode == EXTD_SINGLE_LAYER_MODE || ext_disp_mode == EXTD_RDMA_DPI_MODE) return 0; else return 1; } /* * static int _is_dsc_enable(unsigned int session) { int ret = 0; if (DISP_SESSION_DEV(session) == DEV_LCM) ret = extd_lcm_params.dsi.dsc_enable; else ret = extd_lcm_params.dpi.dsc_enable; return ret; } */ static int _build_path_direct_link(unsigned int session) { int ret = 0; struct m4u_port_config_struct sPort; EXTDFUNC(); pgc->mode = EXTD_DIRECT_LINK_MODE; pgc->dpmgr_handle = dpmgr_create_path(DDP_SCENARIO_SUB_DISP, pgc->cmdq_handle_config); if (pgc->dpmgr_handle) EXTDINFO("dpmgr create path SUCCESS(%p)\n", pgc->dpmgr_handle); else { EXTDERR("dpmgr create path FAIL\n"); return -1; } sPort.ePortID = M4U_PORT_UNKNOWN; sPort.Virtuality = ext_disp_use_m4u; sPort.Security = 0; sPort.Distance = 1; sPort.Direction = 0; ret = m4u_config_port(&sPort); if (ret == 0) { EXTDINFO("config M4U Port %s to %s SUCCESS\n", ddp_get_module_name(DISP_MODULE_OVL1_2L), ext_disp_use_m4u ? "virtual" : "physical"); } else { EXTDERR("config M4U Port %s to %s FAIL(ret=%d)\n", "ovl1_2l", ext_disp_use_m4u ? "virtual" : "physical", ret); return -1; } dpmgr_set_lcm_utils(pgc->dpmgr_handle, NULL); dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC); dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE); dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_START); return ret; } static int _build_path_decouple(void) { return 0; } static int _build_path_single_layer(void) { return 0; } static int _build_path_rdma_dpi(void) { int ret = 0; struct m4u_port_config_struct sPort; EXTDFUNC(); pgc->mode = EXTD_RDMA_DPI_MODE; pgc->dpmgr_handle = dpmgr_create_path(DDP_SCENARIO_SUB_RDMA1_DISP, pgc->cmdq_handle_config); if (pgc->dpmgr_handle) EXTDINFO("dpmgr create path SUCCESS(%p)\n", pgc->dpmgr_handle); else { EXTDERR("dpmgr create path FAIL\n"); return -1; } sPort.ePortID = M4U_PORT_UNKNOWN; sPort.Virtuality = ext_disp_use_m4u; sPort.Security = 0; sPort.Distance = 1; sPort.Direction = 0; ret = m4u_config_port(&sPort); if (ret == 0) { EXTDINFO("config M4U Port %s to %s SUCCESS\n", ddp_get_module_name(DISP_MODULE_RDMA1), ext_disp_use_m4u ? "virtual" : "physical"); } else { EXTDERR("config M4U Port %s to %s FAIL(ret=%d)\n", ddp_get_module_name(DISP_MODULE_RDMA1), ext_disp_use_m4u ? "virtual" : "physical", ret); return -1; } dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC); dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE); return ret; } static void _cmdq_build_trigger_loop(void) { int ret = 0; EXTDFUNC(); if (pgc->cmdq_handle_trigger == NULL) { ret = cmdqRecCreate(CMDQ_SCENARIO_TRIGGER_LOOP, &(pgc->cmdq_handle_trigger)); if (ret) { EXTDERR("%s:%d, create cmdq handle fail!ret=%d\n", __func__, __LINE__, ret); ASSERT(0); } } /* Set fake cmdq engineflag for judge path scenario */ cmdqRecSetEngine(pgc->cmdq_handle_trigger, ((1LL << CMDQ_ENG_DISP_OVL1) | (1LL << CMDQ_ENG_DISP_WDMA1))); cmdqRecReset(pgc->cmdq_handle_trigger); if (ext_disp_is_video_mode()) { /* wait and clear stream_done, */ /* HW will assert mutex enable automatically */ /* in frame done reset. */ /* todo: should let dpmanager to decide */ /* wait which mutex's eof. */ ret = cmdqRecWait(pgc->cmdq_handle_trigger, dpmgr_path_get_mutex(pgc->dpmgr_handle) + CMDQ_EVENT_MUTEX0_STREAM_EOF); /* for some moduleto read hw register to GPR after frame done */ dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_trigger, CMDQ_AFTER_STREAM_EOF, 0); } else { /* DSI command mode need use CMDQ token instead */ ret = cmdqRecWait(pgc->cmdq_handle_trigger, CMDQ_SYNC_TOKEN_EXT_CONFIG_DIRTY); dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_trigger, CMDQ_WAIT_LCM_TE, 0); ret = cmdqRecWaitNoClear(pgc->cmdq_handle_trigger, CMDQ_SYNC_TOKEN_EXT_CABC_EOF); /* cleat frame done token, */ /* now the config thread will not allowed */ /* to config registers. */ /* remember that config thread's priority */ /* is higher than trigger thread */ /* so all the config queued before will be applied */ /* then STREAM_EOF token be cleared */ /* this is what CMDQ did as "Merge" */ ret = cmdqRecClearEventToken(pgc->cmdq_handle_trigger, CMDQ_SYNC_TOKEN_EXT_STREAM_EOF); ret = cmdqRecClearEventToken(pgc->cmdq_handle_trigger, CMDQ_SYNC_TOKEN_EXT_CONFIG_DIRTY); /* clear rdma EOF token before wait */ ret = cmdqRecClearEventToken(pgc->cmdq_handle_trigger, CMDQ_EVENT_DISP_RDMA1_EOF); /* for operations before frame transfer, */ /* such as waiting for DSI TE */ dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_trigger, CMDQ_BEFORE_STREAM_SOF, 0); /* enable mutex, only cmd mode need this */ /* this is what CMDQ did as "Trigger" */ dpmgr_path_trigger(pgc->dpmgr_handle, pgc->cmdq_handle_trigger, CMDQ_ENABLE); dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_trigger, CMDQ_AFTER_STREAM_SOF, 1); /* waiting for frame done, */ /* because we can't use mutex stream eof here */ /* so need to let dpmanager help to */ /* decide which event to wait */ /* most time we wait rdmax frame done event. */ ret = cmdqRecWait(pgc->cmdq_handle_trigger, CMDQ_EVENT_DISP_RDMA1_EOF); dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_trigger, CMDQ_WAIT_STREAM_EOF_EVENT, 0); /* dsi is not idle rightly after rdma frame done, */ /* so we need to polling about 1us for dsi returns to idle */ /* do not polling dsi idle directly */ /* which will decrease CMDQ performance */ dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_trigger, CMDQ_CHECK_IDLE_AFTER_STREAM_EOF, 0); /* for some module to read hw register after frame done */ dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_trigger, CMDQ_AFTER_STREAM_EOF, 0); /* reset some modules to enhance robusty */ dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_trigger, CMDQ_RESET_AFTER_STREAM_EOF, 0); /* now frame done, */ /* config thread is allowed to config register now */ ret = cmdqRecSetEventToken(pgc->cmdq_handle_trigger, CMDQ_SYNC_TOKEN_EXT_STREAM_EOF); ret = cmdqRecSetEventToken(pgc->cmdq_handle_trigger, CMDQ_SYNC_TOKEN_EXT_CABC_EOF); /* RUN forever!!!! */ WARN_ON(ret < 0); } /* dump trigger loop instructions to check */ /* whether dpmgr_path_build_cmdq works correctly */ cmdqRecDumpCommand(pgc->cmdq_handle_trigger); EXTDMSG("ext display BUILD cmdq trigger loop finished\n"); } void _cmdq_start_extd_trigger_loop(void) { int ret = 0; EXTDFUNC(); ret = cmdqRecStartLoop(pgc->cmdq_handle_trigger); if (!ext_disp_is_video_mode()) { cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_EXT_STREAM_EOF); cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_EXT_CABC_EOF); } EXTDMSG("START cmdq trigger loop finished\n"); } void _cmdq_stop_extd_trigger_loop(void) { int ret = 0; EXTDFUNC(); ret = cmdqRecStopLoop(pgc->cmdq_handle_trigger); EXTDMSG("ext display STOP cmdq trigger loop finished\n"); } static void _cmdq_set_config_handle_dirty(void) { if (!ext_disp_is_video_mode()) { /* only command mode need to set dirty */ cmdqRecSetEventToken(pgc->cmdq_handle_config, CMDQ_SYNC_TOKEN_EXT_CONFIG_DIRTY); /* /dprec_event_op(DPREC_EVENT_CMDQ_SET_DIRTY); */ } } static void _cmdq_handle_clear_dirty(struct cmdqRecStruct *cmdq_handle) { if (!ext_disp_is_video_mode()) cmdqRecClearEventToken(cmdq_handle, CMDQ_SYNC_TOKEN_EXT_CONFIG_DIRTY); } static void _cmdq_reset_config_handle(void) { cmdqRecReset(pgc->cmdq_handle_config); /* /dprec_event_op(DPREC_EVENT_CMDQ_RESET); */ } static void _cmdq_flush_config_handle(int blocking, void *callback, unsigned int userdata) { if (blocking) { /* it will be blocked until mutex done */ cmdqRecFlush(pgc->cmdq_handle_config); } else { if (callback) cmdqRecFlushAsyncCallback(pgc->cmdq_handle_config, callback, userdata); else cmdqRecFlushAsync(pgc->cmdq_handle_config); } /* dprec_event_op(DPREC_EVENT_CMDQ_FLUSH); */ } #if 0 static void _cmdq_insert_wait_frame_done_token(int clear_event) { if (ext_disp_is_video_mode()) { if (clear_event == 0) { cmdqRecWaitNoClear(pgc->cmdq_handle_config, dpmgr_path_get_mutex( pgc->dpmgr_handle) + CMDQ_EVENT_MUTEX0_STREAM_EOF); } else { cmdqRecWait(pgc->cmdq_handle_config, dpmgr_path_get_mutex(pgc->dpmgr_handle) + CMDQ_EVENT_MUTEX0_STREAM_EOF); } } else { if (clear_event == 0) cmdqRecWaitNoClear(pgc->cmdq_handle_config, CMDQ_SYNC_TOKEN_EXT_STREAM_EOF); else cmdqRecWait(pgc->cmdq_handle_config, CMDQ_SYNC_TOKEN_EXT_STREAM_EOF); } /* /dprec_event_op(DPREC_EVENT_CMDQ_WAIT_STREAM_EOF); */ } #endif void _ext_cmdq_insert_wait_frame_done_token(void *handle) { if (ext_disp_is_video_mode()) { cmdqRecWaitNoClear(handle, dpmgr_path_get_mutex(pgc->dpmgr_handle) + CMDQ_EVENT_MUTEX0_STREAM_EOF); } else cmdqRecWaitNoClear(handle, CMDQ_SYNC_TOKEN_EXT_STREAM_EOF); } static int _convert_disp_input_to_rdma(struct RDMA_CONFIG_STRUCT *dst, struct disp_input_config *src, unsigned int screen_w, unsigned int screen_h) { unsigned int Bpp = 0; unsigned long mva_offset = 0; enum UNIFIED_COLOR_FMT tmp_fmt; if (!src || !dst) { EXTDERR("%s src(0x%p) or dst(0x%p) is null\n", __func__, src, dst); return -1; } if (!src->layer_enable) return 0; dst->idx = src->next_buff_idx; tmp_fmt = disp_fmt_to_unified_fmt(src->src_fmt); ufmt_disable_X_channel(tmp_fmt, &dst->inputFormat, NULL); Bpp = UFMT_GET_Bpp(dst->inputFormat); mva_offset = (src->src_offset_x + src->src_offset_y * src->src_pitch) * Bpp; dst->address = (unsigned long)src->src_phy_addr + mva_offset; dst->pitch = src->src_pitch * Bpp; dst->width = min(src->src_width, src->tgt_width); dst->height = min(src->src_height, src->tgt_height); dst->security = src->security; dst->yuv_range = src->yuv_range; dst->dst_y = src->tgt_offset_y; dst->dst_x = src->tgt_offset_x; dst->dst_h = screen_h; dst->dst_w = screen_w; return 0; } static int _convert_disp_input_to_ovl(struct OVL_CONFIG_STRUCT *dst, struct disp_input_config *src) { int force_disable_alpha = 0; enum UNIFIED_COLOR_FMT tmp_fmt; unsigned int Bpp = 0; if (!src || !dst) { EXTDERR("%s src(0x%p) or dst(0x%p) is null\n", __func__, src, dst); return -1; } dst->layer = src->layer_id; dst->isDirty = 1; dst->buff_idx = src->next_buff_idx; dst->layer_en = src->layer_enable; /* if layer is disable, we just needs config above params. */ if (!src->layer_enable) return 0; tmp_fmt = disp_fmt_to_unified_fmt(src->src_fmt); /* display don't support X channel, like XRGB8888 * we need to enable const_bld */ ufmt_disable_X_channel(tmp_fmt, &dst->fmt, &dst->const_bld); #if 0 if (tmp_fmt != dst->fmt) force_disable_alpha = 1; #endif Bpp = UFMT_GET_Bpp(dst->fmt); dst->addr = (unsigned long)src->src_phy_addr; dst->vaddr = (unsigned long)src->src_base_addr; dst->src_x = src->src_offset_x; dst->src_y = src->src_offset_y; dst->src_w = src->src_width; dst->src_h = src->src_height; dst->src_pitch = src->src_pitch * Bpp; dst->dst_x = src->tgt_offset_x; dst->dst_y = src->tgt_offset_y; /* dst W/H should <= src W/H */ dst->dst_w = min(src->src_width, src->tgt_width); dst->dst_h = min(src->src_height, src->tgt_height); dst->keyEn = src->src_use_color_key; dst->key = src->src_color_key; dst->aen = force_disable_alpha ? 0 : src->alpha_enable; dst->sur_aen = force_disable_alpha ? 0 : src->sur_aen; dst->alpha = src->alpha; dst->src_alpha = src->src_alpha; dst->dst_alpha = src->dst_alpha; dst->identity = src->identity; dst->connected_type = src->connected_type; dst->security = src->security; dst->yuv_range = src->yuv_range; if (src->buffer_source == DISP_BUFFER_ALPHA) { dst->source = OVL_LAYER_SOURCE_RESERVED; } else if (src->buffer_source == DISP_BUFFER_ION || src->buffer_source == DISP_BUFFER_MVA) { dst->source = OVL_LAYER_SOURCE_MEM; } else { EXTDERR("unknown source = %d", src->buffer_source); dst->source = OVL_LAYER_SOURCE_MEM; } dst->ext_sel_layer = src->ext_sel_layer; return 0; } static int _ext_disp_trigger(int blocking, void *callback, unsigned int userdata) { bool reg_flush = false; EXTDFUNC(); if (_should_wait_path_idle()) dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ / 2); if (_should_start_path()) { reg_flush = true; dpmgr_path_start(pgc->dpmgr_handle, ext_disp_cmdq_enabled()); mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_PULSE, Trigger, 1); } if (_should_set_cmdq_dirty()) _cmdq_set_config_handle_dirty(); if (_should_flush_cmdq_config_handle()) { if (reg_flush) mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_PULSE, Trigger, 2); _cmdq_flush_config_handle(blocking, callback, userdata); } if (_should_trigger_path()) { /* trigger_loop_handle is used only for build trigger loop */ dpmgr_path_trigger(pgc->dpmgr_handle, NULL, ext_disp_cmdq_enabled()); } if (_should_reset_cmdq_config_handle()) _cmdq_reset_config_handle(); if (_should_insert_wait_frame_done_token()) _ext_cmdq_insert_wait_frame_done_token(pgc->cmdq_handle_config); EXTDINFO("%s done\n", __func__); return 0; } static int _ext_disp_trigger_EPD(int blocking, void *callback, unsigned int userdata) { EXTDFUNC(); if (_should_wait_path_idle()) dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ / 2); if (_should_start_path()) { dpmgr_path_start(pgc->dpmgr_handle, ext_disp_cmdq_enabled()); mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_PULSE, Trigger, 1); } if (_should_set_cmdq_dirty()) _cmdq_set_config_handle_dirty(); if (_should_flush_cmdq_config_handle()) _cmdq_flush_config_handle(blocking, callback, userdata); if (_should_trigger_path()) { /* trigger_loop_handle is used only for build trigger loop */ dpmgr_path_trigger(pgc->dpmgr_handle, NULL, ext_disp_cmdq_enabled()); } if (_should_reset_cmdq_config_handle()) _cmdq_reset_config_handle(); if (_should_insert_wait_frame_done_token()) _ext_cmdq_insert_wait_frame_done_token(pgc->cmdq_handle_config); EXTDINFO("%s done\n", __func__); return 0; } static int _ext_disp_trigger_LCM(int blocking, void *callback, unsigned int userdata) { EXTDFUNC(); if (pgc->lcm_state == EXTD_LCM_NO_INIT) { /* disp_lcm_init(pgc->plcm, 1); */ #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_esd_check_enable(1); #endif pgc->lcm_state = EXTD_LCM_INITED; } if (_should_set_cmdq_dirty()) _cmdq_set_config_handle_dirty(); if (_should_flush_cmdq_config_handle()) _cmdq_flush_config_handle(blocking, callback, userdata); if (_should_trigger_path()) dpmgr_path_trigger(pgc->dpmgr_handle, NULL, ext_disp_cmdq_enabled()); if (_should_reset_cmdq_config_handle()) _cmdq_reset_config_handle(); /* clear cmdq dirty in case trigger loop starts here */ if (_should_set_cmdq_dirty()) _cmdq_handle_clear_dirty(pgc->cmdq_handle_config); if (_should_insert_wait_frame_done_token()) _ext_cmdq_insert_wait_frame_done_token(pgc->cmdq_handle_config); EXTDINFO("%s done\n", __func__); return 0; } static int init_cmdq_slots(cmdqBackupSlotHandle *pSlot, int count, int init_val) { int i; cmdqBackupAllocateSlot(pSlot, count); for (i = 0; i < count; i++) cmdqBackupWriteSlot(*pSlot, i, init_val); return 0; } static void deinit_cmdq_slots(cmdqBackupSlotHandle hSlot) { cmdqBackupFreeSlot(hSlot); } /* * static int ext_disp_cmdq_dump(uint64_t engineFlag, int level) { EXTDFUNC(); if (pgc->dpmgr_handle != NULL) ext_disp_diagnose(); else EXTDMSG("external display dpmgr_handle == NULL\n"); return 0; } */ static int ext_disp_init_hdmi(unsigned int session) { struct disp_ddp_path_config *data_config = NULL; enum EXT_DISP_STATUS ret; enum DISP_MODULE_ENUM dst_module = 0; int isVideoMode = 0; EXTDFUNC(); ret = EXT_DISP_STATUS_OK; ret = cmdqRecCreate(CMDQ_SCENARIO_MHL_DISP, &(pgc->cmdq_handle_config)); if (ret) { EXTDERR("cmdqRecCreate FAIL, ret=%d\n", ret); ret = EXT_DISP_STATUS_ERROR; goto done; } EXTDMSG("cmdqRecCreate SUCCESS, g_cmdq_handle=%p\n", pgc->cmdq_handle_config); /* Set fake cmdq engineflag for judge path scenario */ cmdqRecSetEngine(pgc->cmdq_handle_config, ((1LL << CMDQ_ENG_DISP_OVL1) | (1LL << CMDQ_ENG_DISP_WDMA1))); cmdqRecReset(pgc->cmdq_handle_config); if (dst_is_dsi) dst_module = DISP_MODULE_DSI1; else dst_module = DISP_MODULE_DPI; /* If DP max resolution is 4K, It need use DDP_SCENARIO_SUB_DISP_4K. */ ddp_set_dst_module(DDP_SCENARIO_SUB_DISP, dst_module); if (ext_disp_mode == EXTD_DIRECT_LINK_MODE) _build_path_direct_link(session); else if (ext_disp_mode == EXTD_DECOUPLE_MODE) _build_path_decouple(); else if (ext_disp_mode == EXTD_SINGLE_LAYER_MODE) _build_path_single_layer(); else if (ext_disp_mode == EXTD_RDMA_DPI_MODE) _build_path_rdma_dpi(); else EXTDERR("ext_disp display mode is WRONG\n"); if (ext_disp_use_cmdq == CMDQ_ENABLE) { if (DISP_SESSION_DEV(session) != DEV_EINK + 1) { _cmdq_build_trigger_loop(); _cmdq_start_extd_trigger_loop(); } } pgc->session = session; EXTDINFO("ext_disp display START cmdq trigger loop finished\n"); isVideoMode = ext_disp_is_video_mode(); if (isVideoMode != -1) dpmgr_path_set_video_mode(pgc->dpmgr_handle, isVideoMode); else return -1; dpmgr_path_init(pgc->dpmgr_handle, CMDQ_DISABLE); dpmgr_path_reset(pgc->dpmgr_handle, CMDQ_DISABLE); data_config = dpmgr_path_get_last_config(pgc->dpmgr_handle); if (data_config) { memset((void *)data_config, 0, sizeof(struct disp_ddp_path_config)); memcpy(&(data_config->dispif_config), &extd_lcm_params, sizeof(struct LCM_PARAMS)); if (dst_is_dsi) { data_config->dst_w = extd_lcm_params.width; data_config->dst_h = extd_lcm_params.height; if (extd_lcm_params.dsi.dsc_enable == 1) data_config->dst_w = extd_lcm_params.width; } else { data_config->dst_w = extd_lcm_params.dpi.width; data_config->dst_h = extd_lcm_params.dpi.height; if (extd_lcm_params.dpi.dsc_enable == 1) data_config->dst_w = extd_lcm_params.dpi.width * 3; } data_config->dst_dirty = 1; data_config->p_golden_setting_context = get_golden_setting_pgc(); data_config->p_golden_setting_context->ext_dst_width = data_config->dst_w; data_config->p_golden_setting_context->ext_dst_height = data_config->dst_h; init_roi = 0; ret = dpmgr_path_config(pgc->dpmgr_handle, data_config, NULL); EXTDMSG("%s roi w:%d, h:%d\n", __func__, data_config->dst_w, data_config->dst_h); } else EXTDERR("allocate buffer failed!!!\n"); /* this will be set to always enable cmdq later */ if (ext_disp_is_video_mode()) { if (dst_is_dsi) dpmgr_map_event_to_irq(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC, DDP_IRQ_RDMA1_DONE); else dpmgr_map_event_to_irq(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC, DDP_IRQ_DPI_VSYNC); } if (ext_disp_use_cmdq == CMDQ_ENABLE) _cmdq_reset_config_handle(); atomic_set(&g_extd_trigger_ticket, 1); atomic_set(&g_extd_release_ticket, 0); pgc->state = EXTD_INIT; pgc->ovl_req_state = EXTD_OVL_NO_REQ; done: EXTDMSG("%s done\n", __func__); return ret; } static int ext_disp_init_lcm(char *lcm_name, unsigned int session) { int ret = 0; struct disp_ddp_path_config *data_config = NULL; struct LCM_PARAMS *lcm_param = NULL; EXTDFUNC(); if (pgc->plcm == NULL) { pgc->plcm = plcm_interface; if (pgc->plcm == NULL) { EXTDERR("Does not found lcm!\n"); ret = EXT_DISP_STATUS_ERROR; goto done; } } lcm_param = disp_lcm_get_params(pgc->plcm); if (lcm_param == NULL) { EXTDERR("get lcm params FAILED\n"); ret = EXT_DISP_STATUS_ERROR; goto done; } else { memcpy(&extd_lcm_params, lcm_param, sizeof(struct LCM_PARAMS)); } if (ext_disp_use_cmdq == CMDQ_ENABLE) { ret = cmdqRecCreate(CMDQ_SCENARIO_MHL_DISP, &(pgc->cmdq_handle_config)); if (ret) { EXTDERR("cmdqRecCreate FAIL, ret=%d\n", ret); ret = EXT_DISP_STATUS_ERROR; goto done; } EXTDMSG("cmdqRecCreate SUCCESS, g_cmdq_handle=%p\n", pgc->cmdq_handle_config); /* Set fake cmdq engineflag for judge path scenario */ cmdqRecSetEngine(pgc->cmdq_handle_config, ((1LL << CMDQ_ENG_DISP_OVL1) | (1LL << CMDQ_ENG_DISP_WDMA1))); cmdqRecReset(pgc->cmdq_handle_config); } ddp_set_dst_module(DDP_SCENARIO_SUB_DISP, DISP_MODULE_DSI1); if (ext_disp_mode == EXTD_DIRECT_LINK_MODE) { _build_path_direct_link(session); dpmgr_set_lcm_utils(pgc->dpmgr_handle, pgc->plcm->drv); } else if (ext_disp_mode == EXTD_RDMA_DPI_MODE) { _build_path_rdma_dpi(); dpmgr_set_lcm_utils(pgc->dpmgr_handle, pgc->plcm->drv); } else EXTDERR("ext_disp display mode is WRONG\n"); if (ext_disp_use_cmdq == CMDQ_ENABLE) { _cmdq_build_trigger_loop(); _cmdq_start_extd_trigger_loop(); } dpmgr_path_set_video_mode(pgc->dpmgr_handle, disp_lcm_is_video_mode(pgc->plcm)); dpmgr_path_init(pgc->dpmgr_handle, ext_disp_use_cmdq); dpmgr_path_reset(pgc->dpmgr_handle, ext_disp_use_cmdq); data_config = dpmgr_path_get_last_config(pgc->dpmgr_handle); memset(&(lcm_param->dpi), 0, sizeof(struct LCM_DPI_PARAMS)); memcpy(&(data_config->dispif_config), lcm_param, sizeof(struct LCM_PARAMS)); data_config->dst_w = ext_disp_get_width(session); data_config->dst_h = ext_disp_get_height(session); data_config->p_golden_setting_context = get_golden_setting_pgc(); if (lcm_param->dsi.data_format.format == LCM_DSI_FORMAT_RGB888) data_config->lcm_bpp = 24; else if (lcm_param->dsi.data_format.format == LCM_DSI_FORMAT_RGB565) data_config->lcm_bpp = 16; else if (lcm_param->dsi.data_format.format == LCM_DSI_FORMAT_RGB666) data_config->lcm_bpp = 18; /*data_config->fps = lcm_fps; */ data_config->dst_dirty = 1; ret = dpmgr_path_config(pgc->dpmgr_handle, data_config, ext_disp_use_cmdq ? pgc->cmdq_handle_config : NULL); EXTDMSG("ext_disp_init roi w:%d, h:%d\n", data_config->dst_w, data_config->dst_h); dpmgr_path_start(pgc->dpmgr_handle, ext_disp_use_cmdq); if (disp_lcm_is_video_mode(pgc->plcm)) { /*dpmgr_path_trigger(pgc->dpmgr_handle, NULL, 0); */ dpmgr_map_event_to_irq(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC, DDP_IRQ_RDMA1_DONE); } else #if 0 /* no DSI1 */ dpmgr_map_event_to_irq(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC, DDP_IRQ_DSI1_EXT_TE); #endif if (ext_disp_use_cmdq) { _cmdq_flush_config_handle(0, NULL, 0); _cmdq_reset_config_handle(); _ext_cmdq_insert_wait_frame_done_token(pgc->cmdq_handle_config); } atomic_set(&g_extd_trigger_ticket, 1); atomic_set(&g_extd_release_ticket, 0); #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_lowpower_init(); #endif pgc->state = EXTD_INIT; pgc->ovl_req_state = EXTD_OVL_NO_REQ; pgc->session = session; done: EXTDMSG("%s done\n", __func__); return ret; } void ext_disp_esd_check_lock(void) { mutex_lock(&esd_check_lock); } void ext_disp_esd_check_unlock(void) { mutex_unlock(&esd_check_lock); } /* external display ESD RECOVERY */ int ext_disp_esd_recovery(void) { int ret = 0; struct LCM_PARAMS *lcm_param = NULL; struct cmdqRecStruct *handle = NULL; EXTDFUNC(); _ext_disp_path_lock(__func__); if (pgc->plcm == NULL) { pgc->plcm = plcm_interface; if (pgc->plcm == NULL) { EXTDERR("Does not found lcm!\n"); ret = EXT_DISP_STATUS_ERROR; goto done; } } lcm_param = disp_lcm_get_params(pgc->plcm); if (pgc->state != EXTD_RESUME) { EXTDERR ("[ESD]esd recovery but extd path is slept?\n"); goto done; } #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_idlemgr_kick((char *)__func__, 0); #endif mmprofile_log_ex(ddp_mmp_get_events()->esd_recovery_t, MMPROFILE_FLAG_PULSE, 0, 2); /* blocking flush before stop trigger loop */ ret = cmdqRecCreate(CMDQ_SCENARIO_MHL_DISP, &handle); if (ret) { EXTDERR("%s:%d, create cmdq handle fail!ret=%d\n", __func__, __LINE__, ret); return -1; } /* Set fake cmdq engineflag for judge path scenario */ cmdqRecSetEngine(handle, ((1LL << CMDQ_ENG_DISP_OVL1) | (1LL << CMDQ_ENG_DISP_WDMA1))); cmdqRecReset(handle); _ext_cmdq_insert_wait_frame_done_token(pgc->cmdq_handle_config); cmdqRecFlush(handle); cmdqRecDestroy(handle); EXTDINFO("[ESD]display cmdq trigger loop stop[begin]\n"); _cmdq_stop_extd_trigger_loop(); EXTDINFO("[ESD]display cmdq trigger loop stop[end]\n"); EXTDINFO("[ESD]stop dpmgr path[begin]\n"); dpmgr_path_stop(pgc->dpmgr_handle, CMDQ_DISABLE); EXTDINFO("[ESD]stop dpmgr path[end]\n"); if (dpmgr_path_is_busy(pgc->dpmgr_handle)) { EXTDINFO("[ESD]external display path is busy after stop\n"); dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ * 1); EXTDINFO("[ESD]wait frame done ret:%d\n", ret); } EXTDINFO("[ESD]reset display path[begin]\n"); dpmgr_path_reset(pgc->dpmgr_handle, CMDQ_DISABLE); EXTDINFO("[ESD]reset display path[end]\n"); EXTDINFO("[ESD]lcm suspend[begin]\n"); disp_lcm_suspend(pgc->plcm); EXTDINFO("[ESD]lcm force init[begin]\n"); disp_lcm_init(pgc->plcm, 1); EXTDINFO("[ESD]lcm force init[end]\n"); EXTDINFO("[ESD]start dpmgr path[begin]\n"); dpmgr_path_start(pgc->dpmgr_handle, CMDQ_DISABLE); EXTDINFO("[ESD]start dpmgr path[end]\n"); if (dpmgr_path_is_busy(pgc->dpmgr_handle)) { EXTDERR ("[ESD]Error! we didn't trigger but it's already busy\n"); ret = -1; } EXTDINFO("[ESD]start cmdq trigger loop[begin]\n"); _cmdq_start_extd_trigger_loop(); EXTDINFO("[ESD]start cmdq trigger loop[end]\n"); if (disp_lcm_is_video_mode(pgc->plcm)) { /* for video mode, we need to force trigger here */ /* for cmd mode, just set DPREC_EVENT_CMDQ_SET_EVENT_ALLOW */ /* when trigger loop start */ dpmgr_path_trigger(pgc->dpmgr_handle, NULL, CMDQ_DISABLE); } done: _ext_disp_path_unlock(__func__); EXTDINFO("[ESD]ESD recovery end\n"); return ret; } void ext_disp_probe(void) { EXTDFUNC(); #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) if (plcm_interface == NULL) { plcm_interface = disp_ext_lcm_probe(ext_mtkfb_lcm_name, LCM_INTERFACE_NOTDEFINED, 0); if (plcm_interface == NULL) EXTDERR("disp_ext_lcm_probe returns null\n"); else EXTDMSG("disp_ext_lcm_probe SUCCESS. lcm name:%s\n", plcm_interface->drv->name); } #endif ext_disp_use_cmdq = CMDQ_ENABLE; ext_disp_use_m4u = 1; ext_disp_mode = EXTD_DIRECT_LINK_MODE; #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_check_recovery_init(); mutex_init(&esd_check_lock); #endif extd_mutex_init(&(pgc->lock)); } int ext_disp_init(char *lcm_name, unsigned int session) { int ret = 0; EXTDFUNC(); dpmgr_init(); _ext_disp_path_lock(__func__); if (pgc->state == EXTD_DEINIT) { init_cmdq_slots(&(pgc->ext_cur_config_fence), EXTD_OVERLAY_CNT, 0); init_cmdq_slots(&(pgc->ext_subtractor_when_free), EXTD_OVERLAY_CNT, 0); init_cmdq_slots(&(pgc->ext_input_config_info), 1, 0); #ifdef EXTD_DEBUG_SUPPORT init_cmdq_slots(&(pgc->ext_ovl_rdma_status_info), 1, 0); #endif } if (pgc->state != EXTD_DEINIT) EXTDERR("status is not EXTD_DEINIT!\n"); /* Register external session cmdq dump callback */ /* dpmgr_register_cmdq_dump_callback(ext_disp_cmdq_dump); */ if (DISP_SESSION_DEV(session) == DEV_LCM) ret = ext_disp_init_lcm(lcm_name, session); else ret = ext_disp_init_hdmi(session); _ext_disp_path_unlock(__func__); return ret; } int ext_disp_deinit(unsigned int session) { int loop_cnt = 0; EXTDFUNC(); _ext_disp_path_lock(__func__); if (pgc->state == EXTD_DEINIT) goto deinit_exit; while (((atomic_read(&g_extd_trigger_ticket) - atomic_read(&g_extd_release_ticket)) != 1) && (loop_cnt < 10)) { _ext_disp_path_unlock(__func__); usleep_range(5000, 6000); _ext_disp_path_lock(__func__); /* wait the last configuration done */ loop_cnt++; } if (DISP_SESSION_DEV(session) == DEV_LCM) { #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_esd_check_enable(0); #endif if (pgc->state == EXTD_RESUME) { _ext_disp_path_unlock(__func__); ext_disp_suspend(session); _ext_disp_path_lock(__func__); } } if (pgc->state == EXTD_SUSPEND) dpmgr_path_power_on(pgc->dpmgr_handle, CMDQ_DISABLE); dpmgr_path_deinit(pgc->dpmgr_handle, CMDQ_DISABLE); dpmgr_destroy_path_handle(pgc->dpmgr_handle); /* Release present timeline fence */ /*no external display */ /* mtkfb_release_present_timeline_fence(pgc->session); */ cmdqRecDestroy(pgc->cmdq_handle_config); cmdqRecDestroy(pgc->cmdq_handle_trigger); pgc->cmdq_handle_config = NULL; pgc->cmdq_handle_trigger = NULL; if (pgc->state != EXTD_DEINIT) { deinit_cmdq_slots(pgc->ext_cur_config_fence); deinit_cmdq_slots(pgc->ext_subtractor_when_free); deinit_cmdq_slots(pgc->ext_input_config_info); #ifdef EXTD_DEBUG_SUPPORT deinit_cmdq_slots(pgc->ext_ovl_rdma_status_info); #endif } /* Unregister external session cmdq dump callback */ /* dpmgr_unregister_cmdq_dump_callback(ext_disp_cmdq_dump); */ pgc->state = EXTD_DEINIT; deinit_exit: _ext_disp_path_unlock(__func__); EXTDMSG("%s done\n", __func__); return 0; } int ext_disp_wait_for_vsync(void *config, unsigned int session) { int ret = 0; /* EXTDFUNC(); */ if (pgc->state != EXTD_RESUME && pgc->state != EXTD_INIT) { EXTDERR("%s: External display path is suspended\n", __func__); mdelay(20); return -1; } #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) if ((pgc->lcm_state == EXTD_LCM_SUSPEND) || (pgc->lcm_state == EXTD_LCM_NO_INIT)) { EXTDERR("%s: SUB LCM is suspended\n", __func__); return -1; } /* kick idle manager here to ensure sodi is disabled */ /* when screen update begin(not 100% ensure) */ external_display_idlemgr_kick((char *)__func__, 1); #endif ret = dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC, HZ / 10); if (ret == -2) { EXTDERR("vsync for ext display path not enabled yet\n"); return -1; } /*EXTDINFO("ext_disp_wait_for_vsync - vsync signaled\n"); */ return ret; } static int ext_disp_suspend_release_fence(unsigned int session) { unsigned int i = 0; EXTDFUNC(); /* Release input fence */ for (i = 0; i < EXTERNAL_SESSION_INPUT_LAYER_COUNT; i++) { DISPPR_FENCE ("%s sess=0x%x,layerid=%d\n", __func__, session, i); mtkfb_release_layer_fence(session, i); } EXTDINFO("%s done\n", __func__); return 0; } int ext_disp_suspend(unsigned int session) { enum EXT_DISP_STATUS ret = EXT_DISP_STATUS_OK; EXTDFUNC(); #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) ext_disp_esd_check_lock(); #endif _ext_disp_path_lock(__func__); if (pgc->state == EXTD_DEINIT || pgc->state == EXTD_SUSPEND || session != pgc->session) { EXTDERR("status is not EXTD_RESUME or session is not match\n"); goto done; } #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_idlemgr_kick((char *)__func__, 0); #endif pgc->need_trigger_overlay = 0; if (dpmgr_path_is_busy(pgc->dpmgr_handle)) dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ / 10); if (ext_disp_use_cmdq == CMDQ_ENABLE && DISP_SESSION_DEV(session) != DEV_EINK + 1) _cmdq_stop_extd_trigger_loop(); dpmgr_path_stop(pgc->dpmgr_handle, CMDQ_DISABLE); if (dpmgr_path_is_busy(pgc->dpmgr_handle)) dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ / 30); dpmgr_path_reset(pgc->dpmgr_handle, CMDQ_DISABLE); if (DISP_SESSION_DEV(session) == DEV_LCM) { #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_esd_check_enable(0); #endif EXTDMSG("lcm suspend[begin]\n"); disp_lcm_suspend(pgc->plcm); EXTDMSG("lcm suspend[end]\n"); pgc->lcm_state = EXTD_LCM_SUSPEND; ext_disp_suspend_release_fence(session); } dpmgr_path_power_off(pgc->dpmgr_handle, CMDQ_DISABLE); /* Unregister external session cmdq dump callback */ /* dpmgr_unregister_cmdq_dump_callback(ext_disp_cmdq_dump); */ pgc->state = EXTD_SUSPEND; #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) ext_disp_set_state(EXTD_SUSPEND); #endif done: _ext_disp_path_unlock(__func__); #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) ext_disp_esd_check_unlock(); #endif EXTDMSG("%s done\n", __func__); return ret; } int ext_disp_resume(unsigned int session) { struct disp_ddp_path_config *data_config; enum EXT_DISP_STATUS ret = EXT_DISP_STATUS_OK; int i = 0; EXTDFUNC(); _ext_disp_path_lock(__func__); if (pgc->state != EXTD_SUSPEND || session != pgc->session) { EXTDERR("EXTD_DEINIT/EXTD_INIT/EXTD_RESUME\n"); goto done; } /* Register external session cmdq dump callback */ /* dpmgr_register_cmdq_dump_callback(ext_disp_cmdq_dump); */ init_roi = 1; if (_should_reset_cmdq_config_handle() && DISP_SESSION_DEV(session) != DEV_EINK + 1) _cmdq_reset_config_handle(); dpmgr_path_init(pgc->dpmgr_handle, CMDQ_DISABLE); dpmgr_path_reset(pgc->dpmgr_handle, CMDQ_DISABLE); if (DISP_SESSION_DEV(session) == DEV_LCM) { data_config = dpmgr_path_get_last_config(pgc->dpmgr_handle); memcpy(&(data_config->dispif_config), &extd_lcm_params, sizeof(struct LCM_PARAMS)); data_config->dst_w = ext_disp_get_width(session); data_config->dst_h = ext_disp_get_height(session); data_config->p_golden_setting_context = get_golden_setting_pgc(); if (extd_lcm_params.dsi.data_format.format == LCM_DSI_FORMAT_RGB888) data_config->lcm_bpp = 24; else if (extd_lcm_params.dsi.data_format.format == LCM_DSI_FORMAT_RGB565) data_config->lcm_bpp = 16; else if (extd_lcm_params.dsi.data_format.format == LCM_DSI_FORMAT_RGB666) data_config->lcm_bpp = 18; /*data_config->fps = lcm_fps; */ data_config->dst_dirty = 1; /* disable all ovl layers to show black screen */ for (i = 0; i < ARRAY_SIZE(data_config->ovl_config); i++) data_config->ovl_config[i].layer_en = 0; data_config->ovl_dirty = 1; ret = dpmgr_path_config(pgc->dpmgr_handle, data_config, NULL); data_config->dst_dirty = 0; if (dpmgr_path_is_busy(pgc->dpmgr_handle)) { EXTDERR ("Error! we didn't start but already busy\n"); ret = EXT_DISP_STATUS_ERROR; } if (DISP_SESSION_DEV(session) == DEV_LCM) { EXTDMSG("[POWER]lcm resume[begin]\n"); disp_lcm_resume(pgc->plcm); EXTDMSG("[POWER]lcm resume[end]\n"); #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_esd_check_enable(1); #endif pgc->lcm_state = EXTD_LCM_RESUME; } dpmgr_path_start(pgc->dpmgr_handle, CMDQ_DISABLE); if (ext_disp_use_cmdq == CMDQ_ENABLE) _cmdq_build_trigger_loop(); if (disp_lcm_is_video_mode(pgc->plcm)) { /* for video mode, we need to force trigger here */ /* for cmd mode, just set */ /* DPREC_EVENT_CMDQ_SET_EVENT_ALLOW */ /* when trigger loop start */ if (_should_insert_wait_frame_done_token()) _ext_cmdq_insert_wait_frame_done_token (pgc->cmdq_handle_config); dpmgr_path_trigger(pgc->dpmgr_handle, NULL, CMDQ_DISABLE); } } if (ext_disp_use_cmdq == CMDQ_ENABLE && DISP_SESSION_DEV(session) != DEV_EINK + 1) _cmdq_start_extd_trigger_loop(); if (DISP_SESSION_DEV(session) != DEV_LCM) { if (dpmgr_path_is_busy(pgc->dpmgr_handle)) { EXTDERR("stop display path failed, still busy\n"); ret = -1; goto done; } } if (DISP_SESSION_DEV(session) == DEV_EINK + 1) pgc->suspend_config = 0; pgc->state = EXTD_RESUME; #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) ext_disp_set_state(EXTD_RESUME); #endif done: _ext_disp_path_unlock(__func__); EXTDMSG("%s done\n", __func__); mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_PULSE, Resume, 1); return ret; } int ext_fence_release_callback(unsigned long userdata) { int i = 0; int ret = 0; int fence_idx = 0; int subtractor = 0; unsigned int input_config_info = 0; #ifdef EXTD_DEBUG_SUPPORT unsigned int status = 0; #endif EXTDFUNC(); cmdqBackupReadSlot(pgc->ext_input_config_info, 0, &input_config_info); if (input_config_info & 0x100) { /*input source is rdma */ if (ext_disp_get_ovl_req_status(pgc->session) == EXTD_OVL_REMOVED) ext_disp_path_change(EXTD_OVL_NO_REQ, pgc->session); } else { if (ext_disp_get_ovl_req_status(pgc->session) == EXTD_OVL_INSERTED) ext_disp_path_change(EXTD_OVL_NO_REQ, pgc->session); } _ext_disp_path_lock(__func__); for (i = 0; i < EXTD_OVERLAY_CNT; i++) { cmdqBackupReadSlot(pgc->ext_cur_config_fence, i, &fence_idx); cmdqBackupReadSlot(pgc->ext_subtractor_when_free, i, &subtractor); mtkfb_release_fence(pgc->session, i, fence_idx - subtractor); mmprofile_log_ex(ddp_mmp_get_events()->Extd_UsedBuff, MMPROFILE_FLAG_PULSE, fence_idx, i); } /* Release present fence */ #if 0 if (gCurrentPresentFenceIndex != -1) mtkfb_release_present_fence(pgc->session, gCurrentPresentFenceIndex); #endif #ifdef EXTD_DEBUG_SUPPORT /* check last ovl/rdma status: should be idle when config */ cmdqBackupReadSlot(pgc->ext_ovl_rdma_status_info, 0, &status); if (input_config_info & 0x100) { /*input source is rdma */ if ((status & 0x1000) != 0) { /* rdma smi is not idle !! */ EXTDERR("extd rdma-smi status error!! stat=0x%x\n", status); ext_disp_diagnose(); ret = -1; } } else { if ((status & 0x1) != 0) { /* ovl is not idle !! */ EXTDERR("extd ovl status error!! stat=0x%x\n", status); ext_disp_diagnose(); ret = -1; } } #endif #if defined(CONFIG_MTK_HDMI_SUPPORT) if (pgc->state == EXTD_RESUME) /* hdmi video config with layer_type */ external_display_util.hdmi_video_format_config (input_config_info & 0xff); else EXTDMSG ("%s ext display is not resume\n", __func__); #endif atomic_set(&g_extd_release_ticket, userdata); _ext_disp_path_unlock(__func__); EXTDINFO("%s done\n", __func__); return ret; } int ext_disp_trigger(int blocking, void *callback, unsigned int userdata, unsigned int session) { int ret = 0; /* EXTDFUNC(); */ _ext_disp_path_lock(__func__); if (pgc->state == EXTD_DEINIT || pgc->state == EXTD_SUSPEND || pgc->need_trigger_overlay < 1) { EXTDERR("trigger ext display is already slept\n"); mmprofile_log_ex(ddp_mmp_get_events()->Extd_ErrorInfo, MMPROFILE_FLAG_PULSE, Trigger, 0); _ext_disp_path_unlock(__func__); return -1; } #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_idlemgr_kick((char *)__func__, 0); #endif if (pgc->mode == EXTD_DECOUPLE_MODE) ret = dpmgr_path_trigger(pgc->ovl2mem_path_handle, NULL, ext_disp_use_cmdq); else if (DISP_SESSION_TYPE(session) == DISP_SESSION_EXTERNAL && DISP_SESSION_DEV(session) == DEV_MHL + 1) ret = _ext_disp_trigger(blocking, callback, atomic_read(&g_extd_trigger_ticket)); else if (DISP_SESSION_TYPE(session) == DISP_SESSION_EXTERNAL && DISP_SESSION_DEV(session) == DEV_EINK + 1) ret = _ext_disp_trigger_EPD(blocking, callback, atomic_read(&g_extd_trigger_ticket)); else if (DISP_SESSION_TYPE(session) == DISP_SESSION_EXTERNAL && DISP_SESSION_DEV(session) == DEV_LCM) ret = _ext_disp_trigger_LCM(blocking, callback, atomic_read(&g_extd_trigger_ticket)); else goto done; atomic_add(1, &g_extd_trigger_ticket); pgc->state = EXTD_RESUME; done: _ext_disp_path_unlock(__func__); /* EXTDINFO("ext_disp_trigger done\n"); */ return ret; } int ext_disp_suspend_trigger(void *callback, unsigned int userdata, unsigned int session) { enum EXT_DISP_STATUS ret = EXT_DISP_STATUS_OK; EXTDFUNC(); _ext_disp_path_lock(__func__); if (pgc->state != EXTD_RESUME) { EXTDERR("trigger ext display is already slept\n"); mmprofile_log_ex(ddp_mmp_get_events()->Extd_ErrorInfo, MMPROFILE_FLAG_PULSE, Trigger, 0); _ext_disp_path_unlock(__func__); return -1; } mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_PULSE, Suspend, 0); if (_should_reset_cmdq_config_handle()) _cmdq_reset_config_handle(); if (_should_insert_wait_frame_done_token()) _ext_cmdq_insert_wait_frame_done_token(pgc->cmdq_handle_config); pgc->need_trigger_overlay = 0; if (ext_disp_use_cmdq == CMDQ_ENABLE && DISP_SESSION_DEV(session) != DEV_EINK + 1) _cmdq_stop_extd_trigger_loop(); dpmgr_path_stop(pgc->dpmgr_handle, ext_disp_cmdq_enabled()); if (_should_flush_cmdq_config_handle()) { _cmdq_flush_config_handle(1, 0, 0); _cmdq_reset_config_handle(); } dpmgr_path_reset(pgc->dpmgr_handle, CMDQ_DISABLE); dpmgr_path_power_off(pgc->dpmgr_handle, CMDQ_DISABLE); if (DISP_SESSION_DEV(session) == DEV_EINK + 1) pgc->suspend_config = 1; pgc->state = EXTD_SUSPEND; _ext_disp_path_unlock(__func__); mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_PULSE, Suspend, 1); return ret; } int ext_disp_frame_cfg_input(struct disp_frame_cfg_t *cfg) { int ret = 0; int i = 0; int layer_cnt = 0; int config_layer_id = 0; struct m4u_port_config_struct sPort; struct disp_ddp_path_config *data_config; unsigned int ext_last_fence, ext_cur_fence, ext_sub, input_source; struct ddp_io_golden_setting_arg gset_arg; unsigned int session = cfg->session_id; EXTDFUNC(); _ext_disp_path_lock(__func__); if (pgc->state != EXTD_INIT && pgc->state != EXTD_RESUME && pgc->suspend_config != 1) { EXTDERR("config ext disp is already slept, state:%d\n", pgc->state); mmprofile_log_ex(ddp_mmp_get_events()->Extd_ErrorInfo, MMPROFILE_FLAG_PULSE, Config, cfg->input_cfg[0].next_buff_idx); _ext_disp_path_unlock(__func__); return -2; } #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_idlemgr_kick((char *)__func__, 0); #endif for (i = 0; i < cfg->input_layer_num; i++) { if (cfg->input_cfg[i].layer_enable) layer_cnt++; } if (layer_cnt == 1) { ext_disp_path_change(EXTD_OVL_REMOVE_REQ, session); if (ext_disp_get_ovl_req_status(session) == EXTD_OVL_REMOVE_REQ) { EXTDINFO("config M4U Port DISP_MODULE_RDMA1\n"); sPort.ePortID = M4U_PORT_UNKNOWN; sPort.Virtuality = 1; sPort.Security = 0; sPort.Distance = 1; sPort.Direction = 0; ret = m4u_config_port(&sPort); if (ret != 0) EXTDERR ("config M4U Port %d FAIL\n", sPort.ePortID); if (DISP_SESSION_DEV(session) == DEV_LCM) ddp_set_dst_module(DDP_SCENARIO_SUB_RDMA1_DISP, DISP_MODULE_DSI1); ext_disp_path_set_mode(EXTD_RDMA_DPI_MODE, session); pgc->ovl_req_state = EXTD_OVL_REMOVING; } } else if (layer_cnt > 1) { ext_disp_path_change(EXTD_OVL_INSERT_REQ, session); if (ext_disp_get_ovl_req_status(session) == EXTD_OVL_INSERT_REQ) { EXTDINFO("config M4U Port DISP_MODULE_OVL1_2L\n"); sPort.ePortID = M4U_PORT_UNKNOWN; sPort.Virtuality = 1; sPort.Security = 0; sPort.Distance = 1; sPort.Direction = 0; ret = m4u_config_port(&sPort); if (ret != 0) EXTDERR ("config M4U Port DISP_MODULE_OVL1 FAIL\n"); if (DISP_SESSION_DEV(session) == DEV_LCM) ddp_set_dst_module(DDP_SCENARIO_SUB_DISP, DISP_MODULE_DSI1); ext_disp_path_set_mode(EXTD_DIRECT_LINK_MODE, session); pgc->ovl_req_state = EXTD_OVL_INSERTING; } } #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) external_display_idlemgr_kick((char *)__func__, 0); #endif /* all dirty should be cleared in dpmgr_path_get_last_config() */ data_config = dpmgr_path_get_last_config(pgc->dpmgr_handle); if (dst_is_dsi) { data_config->dst_w = extd_lcm_params.width; data_config->dst_h = extd_lcm_params.height; if (extd_lcm_params.dsi.dsc_enable == 1) data_config->dst_w = extd_lcm_params.width; } else { data_config->dst_w = ext_disp_get_width(session); data_config->dst_h = ext_disp_get_height(session); if (extd_lcm_params.dpi.dsc_enable == 1) data_config->dst_w = extd_lcm_params.dpi.width * 3; } /* hope we can use only 1 input struct for input config, */ /* just set layer number */ if (_should_config_ovl_input()) { for (i = 0; i < cfg->input_layer_num; i++) { config_layer_id = cfg->input_cfg[i].layer_id; ret = _convert_disp_input_to_ovl(& (data_config->ovl_config [config_layer_id]), &(cfg->input_cfg[i])); dprec_mmp_dump_ovl_layer(& (data_config->ovl_config [config_layer_id]), config_layer_id, 2); if (init_roi == 1) { memcpy(&(data_config->dispif_config), &extd_lcm_params, sizeof(struct LCM_PARAMS)); if (DISP_SESSION_DEV(session) == DEV_LCM) EXTDINFO("set dest w:%d, h:%d\n", data_config->dst_w, data_config->dst_h); else EXTDINFO("set dest w:%d, h:%d\n", extd_lcm_params.dpi.width, extd_lcm_params.dpi.height); data_config->dst_dirty = 1; data_config->rdma_config.address = 0; } data_config->ovl_dirty = 1; pgc->need_trigger_overlay = 1; } input_source = 0; memset(&gset_arg, 0, sizeof(gset_arg)); gset_arg.dst_mod_type = dpmgr_path_get_dst_module_type(pgc->dpmgr_handle); gset_arg.is_decouple_mode = 1; dpmgr_path_ioctl(pgc->dpmgr_handle, pgc->cmdq_handle_config, DDP_OVL_GOLDEN_SETTING, &gset_arg); } else { struct OVL_CONFIG_STRUCT ovl_config; _convert_disp_input_to_ovl(&ovl_config, &(cfg->input_cfg[0])); dprec_mmp_dump_ovl_layer(&ovl_config, cfg->input_cfg[0].layer_id, 2); ret = _convert_disp_input_to_rdma(&(data_config->rdma_config), &(cfg->input_cfg[0]), data_config->dst_w, data_config->dst_h); if (data_config->rdma_config.address) { data_config->rdma_dirty = 1; pgc->need_trigger_overlay = 1; } input_source = 1; } if (_should_wait_path_idle()) dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ / 2); memcpy(&(data_config->dispif_config), &extd_lcm_params, sizeof(struct LCM_PARAMS)); ret = dpmgr_path_config(pgc->dpmgr_handle, data_config, ext_disp_cmdq_enabled() ? pgc->cmdq_handle_config : NULL); /* this is used for decouple mode, */ /* to indicate whether we need to trigger ovl */ /* pgc->need_trigger_overlay = 1; */ init_roi = 0; for (i = 0; i < cfg->input_layer_num; i++) { cmdqBackupReadSlot(pgc->ext_cur_config_fence, i, &ext_last_fence); ext_cur_fence = cfg->input_cfg[i].next_buff_idx; if (ext_cur_fence != -1 && ext_cur_fence > ext_last_fence) { cmdqRecBackupUpdateSlot(pgc->cmdq_handle_config, pgc->ext_cur_config_fence, i, ext_cur_fence); } /* for dim_layer/disable_layer/no_fence_layer, */ /* just release all fences configured */ /* for other layers, release current_fence-1 */ if (cfg->input_cfg[i].buffer_source == DISP_BUFFER_ALPHA || cfg->input_cfg[i].layer_enable == 0 || ext_cur_fence == -1) ext_sub = 0; else ext_sub = 1; cmdqRecBackupUpdateSlot(pgc->cmdq_handle_config, pgc->ext_subtractor_when_free, i, ext_sub); } cmdqRecBackupUpdateSlot(pgc->cmdq_handle_config, pgc->ext_input_config_info, 0, (input_source << 8) | (cfg->input_cfg[0]. layer_type)); /* Update present fence index */ if (cfg->present_fence_idx != (unsigned int)-1) gCurrentPresentFenceIndex = cfg->present_fence_idx; #ifdef EXTD_DEBUG_SUPPORT if (_should_config_ovl_input()) cmdqRecBackupRegisterToSlot(pgc->cmdq_handle_config, pgc->ext_ovl_rdma_status_info, 0, disp_addr_convert (DDP_REG_BASE_DISP_OVL1 + DISP_REG_OVL_STA)); else cmdqRecBackupRegisterToSlot(pgc->cmdq_handle_config, pgc->ext_ovl_rdma_status_info, 0, disp_addr_convert (DISP_REG_RDMA_GLOBAL_CON + DISP_RDMA_INDEX_OFFSET)); #endif if (data_config->ovl_dirty) EXTDINFO ("%s idx:%d -w:%d, h:%d, pitch:%d\n", __func__, cfg->input_cfg[0].next_buff_idx, data_config->ovl_config[0].src_w, data_config->ovl_config[0].src_h, data_config->ovl_config[0].src_pitch); else EXTDINFO ("%s idx:%d -w:%d, h:%d, pitch:%d, mva:0x%lx\n", __func__, cfg->input_cfg[0].next_buff_idx, data_config->rdma_config.width, data_config->rdma_config.height, data_config->rdma_config.pitch, data_config->rdma_config.address); _ext_disp_path_unlock(__func__); return ret; } int ext_disp_get_max_layer(void) { if (_should_config_ovl_input()) return EXTERNAL_SESSION_INPUT_LAYER_COUNT; else return 1; } int ext_disp_is_alive(void) { unsigned int temp = 0; /* EXTDFUNC(); */ _ext_disp_path_lock(__func__); temp = pgc->state; _ext_disp_path_unlock(__func__); return temp; } int ext_disp_is_sleepd(void) { unsigned int temp = 0; /* EXTDFUNC(); */ _ext_disp_path_lock(__func__); temp = (pgc->state == 0) ? 1 : 0; _ext_disp_path_unlock(__func__); return temp; } int ext_disp_get_width(unsigned int session) { int ret = extd_lcm_params.dpi.width; if (DISP_SESSION_DEV(session) == DEV_LCM) { if (pgc->plcm && pgc->plcm->params) ret = pgc->plcm->params->width; } return ret; } int ext_disp_get_height(unsigned int session) { int ret = extd_lcm_params.dpi.height; if (DISP_SESSION_DEV(session) == DEV_LCM) { if (pgc->plcm && pgc->plcm->params) ret = pgc->plcm->params->height; } return ret; } unsigned int ext_disp_get_sess_id(void) { unsigned int session_id = is_context_inited > 0 ? pgc->session : 0; return session_id; } int ext_disp_is_video_mode(void) { int ret = 1; if (pgc->plcm) ret = disp_lcm_is_video_mode(pgc->plcm); return ret; } int ext_disp_diagnose(void) { int ret = 0; if (is_context_inited > 0) { EXTDMSG("%s is_context_inited --%d\n", __func__, is_context_inited); dpmgr_check_status(pgc->dpmgr_handle); } return ret; } enum CMDQ_SWITCH ext_disp_cmdq_enabled(void) { return ext_disp_use_cmdq; } int ext_disp_switch_cmdq(enum CMDQ_SWITCH use_cmdq) { _ext_disp_path_lock(__func__); ext_disp_use_cmdq = use_cmdq; EXTDMSG("display driver use %s to config register now\n", (use_cmdq == CMDQ_ENABLE) ? "CMDQ" : "CPU"); _ext_disp_path_unlock(__func__); return ext_disp_use_cmdq; } void ext_disp_get_curr_addr(unsigned long *input_curr_addr, int module) { dpmgr_get_input_address(pgc->dpmgr_handle, input_curr_addr); } int ext_disp_factory_test(int mode, void *config) { if (mode == 1) dpmgr_factory_mode_reset(DISP_MODULE_DPI, NULL, config); else dpmgr_factory_mode_test(DISP_MODULE_DPI, NULL, config); return 0; } int ext_disp_get_handle(disp_path_handle *dp_handle, struct cmdqRecStruct **pHandle) { *dp_handle = pgc->dpmgr_handle; *pHandle = pgc->cmdq_handle_config; return pgc->mode; } int ext_disp_set_ovl1_status(int status) { /* dpmgr_set_ovl1_status(status);*/ return 0; } int ext_disp_set_lcm_param(struct LCM_PARAMS *pLCMParam) { if (pLCMParam) memcpy(&extd_lcm_params, pLCMParam, sizeof(struct LCM_PARAMS)); return 0; } enum EXTD_OVL_REQ_STATUS ext_disp_get_ovl_req_status(unsigned int session) { enum EXTD_OVL_REQ_STATUS ret = EXTD_OVL_NO_REQ; /* _ext_disp_path_lock();*/ ret = pgc->ovl_req_state; /* _ext_disp_path_unlock();*/ return ret; } int ext_disp_path_change(enum EXTD_OVL_REQ_STATUS action, unsigned int session) { /* EXTDFUNC();*/ if (EXTD_OVERLAY_CNT > 0) { /* _ext_disp_path_lock();*/ switch (action) { case EXTD_OVL_NO_REQ: break; case EXTD_OVL_REQUSTING_REQ: break; case EXTD_OVL_IDLE_REQ: break; case EXTD_OVL_SUB_REQ: break; case EXTD_OVL_REMOVE_REQ: break; case EXTD_OVL_REMOVING: pgc->ovl_req_state = EXTD_OVL_REMOVING; break; case EXTD_OVL_REMOVED: pgc->ovl_req_state = EXTD_OVL_REMOVED; break; case EXTD_OVL_INSERT_REQ: break; case EXTD_OVL_INSERTING: pgc->ovl_req_state = EXTD_OVL_INSERTING; break; case EXTD_OVL_INSERTED: pgc->ovl_req_state = EXTD_OVL_INSERTED; break; default: break; } /* _ext_disp_path_unlock();*/ } return 0; } int ext_disp_wait_ovl_available(int ovl_num) { int ret = 0; if (EXTD_OVERLAY_CNT > 0) { /*wait OVL can be used by external display */ ret = dpmgr_wait_ovl_available(ovl_num); } return ret; } bool ext_disp_path_source_is_RDMA(unsigned int session) { bool is_rdma = false; if ((ext_disp_mode == EXTD_RDMA_DPI_MODE && pgc->ovl_req_state != EXTD_OVL_REMOVE_REQ && pgc->ovl_req_state != EXTD_OVL_REMOVING) || (ext_disp_mode == EXTD_DIRECT_LINK_MODE && pgc->ovl_req_state == EXTD_OVL_INSERT_REQ)) { /* path source module is RDMA */ is_rdma = true; } return is_rdma; } int ext_disp_is_dim_layer(unsigned long mva) { int ret = 0; ret = is_dim_layer(mva); return ret; } void extd_disp_get_interface(struct disp_lcm_handle **plcm) { if (plcm_interface == NULL) { plcm_interface = disp_ext_lcm_probe(NULL, LCM_INTERFACE_NOTDEFINED, 0); if (plcm_interface == NULL) EXTDERR("disp_lcm_probe returns null\n"); } *plcm = plcm_interface; } #if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \ (CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2) static DECLARE_WAIT_QUEUE_HEAD(ext_disp_state_wait_queue); enum EXTD_POWER_STATE ext_disp_get_state(void) { return pgc->state; } enum EXTD_POWER_STATE ext_disp_set_state(enum EXTD_POWER_STATE new_state) { enum EXTD_POWER_STATE old_state = pgc->state; pgc->state = new_state; EXTDINFO("%s %d to %d\n", __func__, old_state, new_state); wake_up(&ext_disp_state_wait_queue); return old_state; } /* use MAX_SCHEDULE_TIMEOUT to wait for ever * NOTES: _ext_disp_path_lock should NOT be held when call this func !!!!!!!! */ #define __ext_disp_wait_state(condition, timeout) \ wait_event_timeout(ext_disp_state_wait_queue, condition, timeout) long ext_disp_wait_state(enum EXTD_POWER_STATE state, long timeout) { long ret; ret = __ext_disp_wait_state(ext_disp_get_state() == state, timeout); return ret; } void *ext_disp_get_dpmgr_handle(void) { return pgc->dpmgr_handle; } static void _cmdq_flush_config_handle_mira(void *handle, int blocking) { if (blocking) cmdqRecFlush(handle); else cmdqRecFlushAsync(handle); } static int _set_backlight_by_cmdq(unsigned int level) { int ret = 0; struct cmdqRecStruct *cmdq_handle_backlight = NULL; EXTDFUNC(); ret = cmdqRecCreate(CMDQ_SCENARIO_MHL_DISP, &cmdq_handle_backlight); if (ret) { EXTDERR("%s:%d, create cmdq handle fail!ret=%d\n", __func__, __LINE__, ret); ret = -1; goto done; } EXTDINFO("external backlight, handle=%p\n", cmdq_handle_backlight); cmdqRecSetEngine(cmdq_handle_backlight, ((1LL << CMDQ_ENG_DISP_OVL1) | (1LL << CMDQ_ENG_DISP_WDMA1))); cmdqRecReset(cmdq_handle_backlight); if (ext_disp_is_video_mode()) { _ext_cmdq_insert_wait_frame_done_token(cmdq_handle_backlight); disp_lcm_set_backlight(pgc->plcm, cmdq_handle_backlight, level); _cmdq_flush_config_handle_mira(cmdq_handle_backlight, 1); EXTDMSG("[BL]%s ret=%d\n", __func__, ret); } else { cmdqRecWait(cmdq_handle_backlight, CMDQ_SYNC_TOKEN_EXT_CABC_EOF); _cmdq_handle_clear_dirty(cmdq_handle_backlight); _ext_cmdq_insert_wait_frame_done_token(cmdq_handle_backlight); disp_lcm_set_backlight(pgc->plcm, cmdq_handle_backlight, level); cmdqRecSetEventToken(cmdq_handle_backlight, CMDQ_SYNC_TOKEN_EXT_CABC_EOF); _cmdq_flush_config_handle_mira(cmdq_handle_backlight, 1); EXTDMSG("[BL]%s ret=%d\n", __func__, ret); } cmdqRecDestroy(cmdq_handle_backlight); cmdq_handle_backlight = NULL; done: return ret; } static int _set_backlight_by_cpu(unsigned int level) { int ret = 0; EXTDFUNC(); if (ext_disp_is_video_mode()) { disp_lcm_set_backlight(pgc->plcm, NULL, level); } else { EXTDMSG("[BL]display cmdq trigger loop stop[begin]\n"); if (ext_disp_cmdq_enabled()) _cmdq_stop_extd_trigger_loop(); EXTDMSG("[BL]display cmdq trigger loop stop[end]\n"); if (dpmgr_path_is_busy(pgc->dpmgr_handle)) { EXTDMSG("[BL]external display path is busy\n"); ret = dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ * 1); EXTDMSG("[BL]wait frame done ret:%d\n", ret); } EXTDMSG("[BL]stop dpmgr path[begin]\n"); dpmgr_path_stop(pgc->dpmgr_handle, CMDQ_DISABLE); EXTDMSG("[BL]stop dpmgr path[end]\n"); if (dpmgr_path_is_busy(pgc->dpmgr_handle)) { EXTDMSG ("[BL]external display path is busy after stop\n"); dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ * 1); EXTDMSG("[BL]wait frame done ret:%d\n", ret); } EXTDMSG("[BL]reset display path[begin]\n"); dpmgr_path_reset(pgc->dpmgr_handle, CMDQ_DISABLE); EXTDMSG("[BL]reset display path[end]\n"); disp_lcm_set_backlight(pgc->plcm, NULL, level); EXTDMSG("[BL]start dpmgr path[begin]\n"); dpmgr_path_start(pgc->dpmgr_handle, CMDQ_DISABLE); EXTDMSG("[BL]start dpmgr path[end]\n"); if (ext_disp_cmdq_enabled()) { EXTDMSG("[BL]start cmdq trigger loop[begin]\n"); _cmdq_start_extd_trigger_loop(); } EXTDMSG("[BL]start cmdq trigger loop[end]\n"); } return ret; } int external_display_setbacklight(unsigned int level) { int ret = 0; static unsigned int last_level; EXTDFUNC(); if (last_level == level) return 0; ext_disp_esd_check_lock(); _ext_disp_path_lock(__func__); if (pgc->state == EXTD_SUSPEND) { EXTDERR("%s: external sleep state set backlight invalid\n", __func__); } else if ((pgc->lcm_state == EXTD_LCM_SUSPEND) || (pgc->lcm_state == EXTD_LCM_NO_INIT)) { EXTDERR("%s: SUB LCM is suspended\n", __func__); } else { external_display_idlemgr_kick((char *)__func__, 0); if (ext_disp_cmdq_enabled()) { if (ext_disp_is_video_mode()) disp_lcm_set_backlight(pgc->plcm, NULL, level); else _set_backlight_by_cmdq(level); } else { _set_backlight_by_cpu(level); } last_level = level; } _ext_disp_path_unlock(__func__); ext_disp_esd_check_unlock(); EXTDINFO("%s done\n", __func__); return ret; } #endif