// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include "extd_info.h" #if defined(CONFIG_MTK_HDMI_SUPPORT) #define _tx_c_ #include #include #include #include #include #include #include #include /* #include */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include */ #ifdef CONFIG_MTK_CLKMGR #include #endif /* #include "mach/irqs.h" */ #include #include #include #ifdef CONFIG_MTK_M4U #include "m4u.h" #endif /* #include "mt-plat/mtk_boot_common.h" */ /* #include "mt-plat/mt_boot.h" */ #include "mtkfb_info.h" #include "mtkfb.h" #include "mtkfb_fence.h" #include "display_recorder.h" #include "ddp_info.h" #include "ddp_irq.h" #include "ddp_mmp.h" #include "disp_session.h" #include "extd_platform.h" #include "extd_hdmi.h" #include "extd_factory.h" #include "extd_log.h" #include "extd_utils.h" #include "extd_hdmi_types.h" #include "external_display.h" #ifdef CONFIG_MTK_SMARTBOOK_SUPPORT # include # include "smartbook.h" #endif #ifdef I2C_DBG # include "tmbslHdmiTx_types.h" # include "tmbslTDA9989_local.h" #endif #ifdef MHL_DYNAMIC_VSYNC_OFFSET # include "ged_dvfs.h" #endif #if defined(CONFIG_MTK_DCS) # include "mt-plat/mtk_meminfo.h" #endif /* the static variable */ static atomic_t hdmi_fake_in = ATOMIC_INIT(false); static int first_frame_done; static int wait_vsync_enable; static bool otg_enable_status; static bool hdmi_vsync_flag; static wait_queue_head_t hdmi_vsync_wq; static unsigned long hdmi_reschange = HDMI_VIDEO_RESOLUTION_NUM; static unsigned long force_reschange = 0xffff; int enable_ut; unsigned int dst_is_dsi; static struct switch_dev hdmi_switch_data; static struct switch_dev hdmires_switch_data; static struct HDMI_DRIVER *hdmi_drv; static struct _t_hdmi_context hdmi_context; static struct _t_hdmi_context *p = &hdmi_context; struct task_struct *hdmi_3d_config_task; wait_queue_head_t hdmi_3d_config_wq; atomic_t hdmi_3d_config_event = ATOMIC_INIT(0); #if defined(CONFIG_MTK_DCS) struct task_struct *dcs_switch_to_4ch_task; wait_queue_head_t dcs_switch_to_4ch_wq; atomic_t dcs_4ch_switch_event = ATOMIC_INIT(0); wait_queue_head_t hdmi_video_config_wq; atomic_t dcs_4ch_switch_done_event = ATOMIC_INIT(0); #endif static unsigned int hdmi_layer_num; static unsigned int hdmi_resolution_param_table[][3] = { {720, 480, 60}, {1280, 720, 60}, {1920, 1080, 30}, {1920, 1080, 60}, }; DEFINE_SEMAPHORE(hdmi_update_mutex); /* global variables */ #ifdef EXTD_DBG_USE_INNER_BUF unsigned long hdmi_va, hdmi_mva_r; #endif struct HDMI_PARAMS _s_hdmi_params; struct HDMI_PARAMS *hdmi_params = &_s_hdmi_params; static int rdmafpscnt; struct disp_ddp_path_config extd_dpi_params; struct task_struct *hdmi_fence_release_task; wait_queue_head_t hdmi_fence_release_wq; atomic_t hdmi_fence_release_event = ATOMIC_INIT(0); struct task_struct *hdmi_wait_vsync_task; /* definition */ #define IS_HDMI_ON() (atomic_read(&p->state) == HDMI_POWER_STATE_ON) #define IS_HDMI_OFF() (atomic_read(&p->state) == HDMI_POWER_STATE_OFF) #define IS_HDMI_STANDBY() (atomic_read(&p->state) == HDMI_POWER_STATE_STANDBY) #define IS_HDMI_NOT_ON() (atomic_read(&p->state) != HDMI_POWER_STATE_ON) #define IS_HDMI_NOT_OFF() (atomic_read(&p->state) != HDMI_POWER_STATE_OFF) #define IS_HDMI_NOT_STANDBY() \ (atomic_read(&p->state) != HDMI_POWER_STATE_STANDBY) #define SET_HDMI_ON() atomic_set(&p->state, HDMI_POWER_STATE_ON) #define SET_HDMI_OFF() atomic_set(&p->state, HDMI_POWER_STATE_OFF) #define SET_HDMI_STANDBY() atomic_set(&p->state, HDMI_POWER_STATE_STANDBY) #define IS_HDMI_FAKE_PLUG_IN() (atomic_read(&hdmi_fake_in) == true) #define SET_HDMI_FAKE_PLUG_IN() (atomic_set(&hdmi_fake_in, true)) #define SET_HDMI_NOT_FAKE() (atomic_set(&hdmi_fake_in, false)) #define MHL_SESSION_ID (0x20001) /* extern declare */ /* extern unsigned char kara_1280x720[2764800]; */ /* Information Dump Routines */ void hdmi_force_on(int from_uart_drv) { } /* params & 0xff: resolution, params & 0xff00 : 3d support */ void hdmi_force_resolution(int params) { force_reschange = params; if ((force_reschange > 0xff) && (force_reschange < 0x0fff)) hdmi_params->is_3d_support = 1; else hdmi_params->is_3d_support = 0; HDMI_LOG("%s params:0x%lx, 3d:%d\n", __func__, force_reschange, hdmi_params->is_3d_support); } #ifdef MM_MHL_DVFS #include "mmdvfs_mgr.h" static void hdmi_enable_dvfs(int enable) { mmdvfs_mhl_enable(enable); } #else static void hdmi_enable_dvfs(int enable) { } #endif /* for debug */ void hdmi_cable_fake_plug_in(void) { SET_HDMI_FAKE_PLUG_IN(); HDMI_LOG("[HDMIFake]Cable Plug In\n"); if (p->is_force_disable == true) return; if (!IS_HDMI_STANDBY()) return; #ifdef MHL_DYNAMIC_VSYNC_OFFSET ged_dvfs_vsync_offset_event_switch(GED_DVFS_VSYNC_OFFSET_MHL_EVENT, true); #endif hdmi_resume(); hdmi_enable_dvfs(true); /* msleep(1000); */ hdmi_reschange = HDMI_VIDEO_RESOLUTION_NUM; switch_set_state(&hdmi_switch_data, HDMI_STATE_ACTIVE); } void hdmi_cable_fake_plug_out(void) { SET_HDMI_NOT_FAKE(); HDMI_LOG("[HDMIFake]Disable\n"); if (p->is_force_disable == false && IS_HDMI_ON()) { if ((hdmi_drv->get_state() != HDMI_STATE_ACTIVE) || (enable_ut == 1)) { #ifdef MHL_DYNAMIC_VSYNC_OFFSET ged_dvfs_vsync_offset_event_switch( GED_DVFS_VSYNC_OFFSET_MHL_EVENT, false); #endif hdmi_suspend(); hdmi_enable_dvfs(false); switch_set_state(&hdmi_switch_data, HDMI_STATE_NO_DEVICE); switch_set_state(&hdmires_switch_data, 0); } } force_reschange = 0xffff; } int hdmi_cable_fake_connect(int connect) { if (connect == 0) hdmi_cable_fake_plug_out(); else hdmi_cable_fake_plug_in(); return 0; } int hdmi_allocate_hdmi_buffer(void) { return 0; } int hdmi_free_hdmi_buffer(void) { return 0; } static int hdmi_wait_vsync_kthread(void *data) { struct disp_session_vsync_config vsync_config; struct sched_param param = { .sched_priority = 94 }; sched_setscheduler(current, SCHED_RR, ¶m); for (;;) { ext_disp_wait_for_vsync((void *)&vsync_config, MHL_SESSION_ID); if (wait_vsync_enable == 0) break; if (kthread_should_stop()) break; } hdmi_wait_vsync_task = NULL; return 0; } int hdmi_wait_vsync_debug(int enable) { wait_vsync_enable = enable; if (enable && hdmi_wait_vsync_task == NULL) { hdmi_wait_vsync_task = kthread_create(hdmi_wait_vsync_kthread, NULL, "hdmi_wait_vsync_kthread"); wake_up_process(hdmi_wait_vsync_task); } return 0; } int hdmi_dump_vendor_chip_register(void) { int ret = 0; if (hdmi_drv && hdmi_drv->dump) hdmi_drv->dump(); return ret; } bool is_hdmi_active(void) { bool active = IS_HDMI_ON() && p->is_clock_on; return active; } unsigned int hdmi_get_width(void) { return p->hdmi_width; } unsigned int hdmi_get_height(void) { return p->hdmi_height; } int hdmi_waitVsync(void) { unsigned int session_id = ext_disp_get_sess_id(); struct disp_session_sync_info *session_info = disp_get_session_sync_info_for_debug(session_id); if (session_info) dprec_start(&session_info->event_waitvsync, 0, 0); if (p->is_clock_on == false) { HDMI_ERR("[hdmi]:hdmi has suspend, return directly\n"); msleep(20); return 0; } hdmi_vsync_flag = 0; if (wait_event_interruptible_timeout(hdmi_vsync_wq, hdmi_vsync_flag, HZ / 10) == 0) HDMI_ERR("[hdmi] Wait VSync timeout. early_suspend=%d\n", p->is_clock_on); if (session_info) dprec_done(&session_info->event_waitvsync, 1, 0); return 0; } int hdmi_get_support_info(void) { int value = 0, temp = 0; #ifdef USING_SCALE_ADJUSTMENT value |= HDMI_SCALE_ADJUSTMENT_SUPPORT; #endif #ifdef MHL_PHONE_GPIO_REUSAGE value |= HDMI_PHONE_GPIO_REUSAGE; #endif #ifdef MTK_AUDIO_MULTI_CHANNEL_SUPPORT temp = hdmi_drv->get_external_device_capablity(); #else temp = 0x2 << 3; #endif value |= temp; value |= HDMI_FACTORY_MODE_NEW; HDMI_LOG("value is 0x%x\n", value); return value; } /* Configure video attribute */ int hdmi_video_config(enum HDMI_VIDEO_RESOLUTION vformat, enum HDMI_VIDEO_INPUT_FORMAT vin, enum HDMI_VIDEO_OUTPUT_FORMAT vout) { if (p->is_mhl_video_on == true && (p->vout == vout)) return 0; HDMI_LOG("%s video_on=%d\n", __func__, p->is_mhl_video_on); if (IS_HDMI_NOT_ON()) { HDMI_LOG("return in %d\n", __LINE__); return 0; } if ((p->is_mhl_video_on == true) && (p->vout != vout)) { p->vout = vout; p->vin = vin; atomic_set(&hdmi_3d_config_event, 1); wake_up_interruptible(&hdmi_3d_config_wq); return 0; } p->vout = vout; p->vin = vin; #if defined(CONFIG_MTK_DCS) if (vformat == HDMI_VIDEO_2160p_DSC_30Hz || vformat == HDMI_VIDEO_2160p_DSC_24Hz) { /* wait DCS switch to 4ch */ wait_event_interruptible(hdmi_video_config_wq, atomic_read (&dcs_4ch_switch_done_event)); atomic_set(&dcs_4ch_switch_done_event, 0); HDMI_LOG("%s wait DCS switch to 4ch done\n", __func__); } #endif p->is_mhl_video_on = true; if (IS_HDMI_FAKE_PLUG_IN()) return 0; return hdmi_drv->video_config(vformat, vin, vout); } /* Configure audio attribute, will be called by audio driver */ int hdmi_audio_config(int format) { enum HDMI_AUDIO_FORMAT audio_format = HDMI_AUDIO_44K_2CH; unsigned int channel_count = format & 0x0F; unsigned int sampleRate = (format & 0x70) >> 4; unsigned int bitWidth = (format & 0x180) >> 7, sampleBit = 0; HDMI_LOG("channel_count: %d, sampleRate: %d, bitWidth: %d\n", channel_count, sampleRate, bitWidth); if (bitWidth == HDMI_MAX_BITWIDTH_16) { sampleBit = 16; HDMI_LOG("HDMI_MAX_BITWIDTH_16\n"); } else if (bitWidth == HDMI_MAX_BITWIDTH_24) { sampleBit = 24; HDMI_LOG("HDMI_MAX_BITWIDTH_24\n"); } if (channel_count == HDMI_MAX_CHANNEL_2 && sampleRate == HDMI_MAX_SAMPLERATE_44) { audio_format = HDMI_AUDIO_44K_2CH; HDMI_LOG("AUDIO_44K_2CH\n"); } else if (channel_count == HDMI_MAX_CHANNEL_2 && sampleRate == HDMI_MAX_SAMPLERATE_48) { audio_format = HDMI_AUDIO_48K_2CH; HDMI_LOG("AUDIO_48K_2CH\n"); } else if (channel_count == HDMI_MAX_CHANNEL_8 && sampleRate == HDMI_MAX_SAMPLERATE_32) { audio_format = HDMI_AUDIO_32K_8CH; HDMI_LOG("AUDIO_32K_8CH\n"); } else if (channel_count == HDMI_MAX_CHANNEL_8 && sampleRate == HDMI_MAX_SAMPLERATE_44) { audio_format = HDMI_AUDIO_44K_8CH; HDMI_LOG("AUDIO_44K_8CH\n"); } else if (channel_count == HDMI_MAX_CHANNEL_8 && sampleRate == HDMI_MAX_SAMPLERATE_48) { audio_format = HDMI_AUDIO_48K_8CH; HDMI_LOG("AUDIO_48K_8CH\n"); } else if (channel_count == HDMI_MAX_CHANNEL_8 && sampleRate == HDMI_MAX_SAMPLERATE_96) { audio_format = HDMI_AUDIO_96K_8CH; HDMI_LOG("AUDIO_96K_8CH\n"); } else if (channel_count == HDMI_MAX_CHANNEL_8 && sampleRate == HDMI_MAX_SAMPLERATE_192) { audio_format = HDMI_AUDIO_192K_8CH; HDMI_LOG("AUDIO_192K_8CH\n"); } else { HDMI_LOG("audio format is not supported\n"); } if (!p->is_enabled) { HDMI_LOG("return in %d\n", __LINE__); return 0; } hdmi_drv->audio_config(audio_format, sampleBit); return 0; } static void _hdmi_rdma_irq_handler(enum DISP_MODULE_ENUM module, unsigned int param) { if (!is_hdmi_active()) return; if (param & 0x2) { /* start */ atomic_set(&hdmi_fence_release_event, 1); wake_up_interruptible(&hdmi_fence_release_wq); if (hdmi_params->cabletype == MHL_SMB_CABLE) { hdmi_vsync_flag = 1; wake_up_interruptible(&hdmi_vsync_wq); } } /* frame done */ if (param & 0x4 && first_frame_done == 0) first_frame_done = 1; } static int hdmi_fence_release_kthread(void *data) { struct sched_param param = { .sched_priority = 94 }; sched_setscheduler(current, SCHED_RR, ¶m); for (;;) { wait_event_interruptible(hdmi_fence_release_wq, atomic_read (&hdmi_fence_release_event)); atomic_set(&hdmi_fence_release_event, 0); rdmafpscnt++; if (kthread_should_stop()) break; } return 0; } static void hdmi_video_format_config(unsigned int layer_3d_format) { if ((force_reschange > 0xff) && (force_reschange < 0x0fff)) layer_3d_format = force_reschange >> 8; if (layer_3d_format >= DISP_LAYER_3D_TAB_0) layer_3d_format = HDMI_VOUT_FORMAT_3D_TAB; else if (layer_3d_format >= DISP_LAYER_3D_SBS_0) layer_3d_format = HDMI_VOUT_FORMAT_3D_SBS; else layer_3d_format = HDMI_VOUT_FORMAT_2D; hdmi_video_config(p->output_video_resolution, HDMI_VIN_FORMAT_RGB888, HDMI_VOUT_FORMAT_RGB888 | layer_3d_format); } static int hdmi_3d_config_kthread(void *data) { struct sched_param param = { .sched_priority = 94 }; enum HDMI_VIDEO_RESOLUTION vformat = HDMI_VIDEO_RESOLUTION_NUM; sched_setscheduler(current, SCHED_RR, ¶m); for (;;) { wait_event_interruptible(hdmi_3d_config_wq, atomic_read(&hdmi_3d_config_event)); atomic_set(&hdmi_3d_config_event, 0); HDMI_LOG("video_on=%d fps %d, %d, %d\n", p->is_mhl_video_on, rdmafpscnt, p->vin, p->vout); if (p->vout >= HDMI_VOUT_FORMAT_2D) hdmi_drv->video_config(vformat, p->vin, p->vout); if (kthread_should_stop()) break; } return 0; } #if defined(CONFIG_MTK_DCS) static int hdmi_dcs_switch_to_4ch_kthread(void *data) { struct sched_param param = { .sched_priority = 94 }; int dcs_channel = 0; enum dcs_status dcs_status; int ret = 0; int i = 10; sched_setscheduler(current, SCHED_RR, ¶m); for (;;) { wait_event_interruptible(dcs_switch_to_4ch_wq, atomic_read(&dcs_4ch_switch_event)); atomic_set(&dcs_4ch_switch_event, 0); /* polling dcs switch done */ i = 10; while (i--) { msleep(100); ret = dcs_get_dcs_status_lock(&dcs_channel, &dcs_status); if (dcs_channel == 4 && dcs_status == 0) { HDMI_LOG("dcs switch success! channel=%d\n", dcs_channel); dcs_get_dcs_status_unlock(); atomic_set(&dcs_4ch_switch_done_event, 1); wake_up_interruptible(&hdmi_video_config_wq); goto done; } dcs_get_dcs_status_unlock(); } /* dcs switch fail */ HDMI_ERR("ERROR! dsc switch fail! channel=%d dcs_status=%d\n", dcs_channel, dcs_status); done: if (kthread_should_stop()) break; } return 0; } #endif static enum HDMI_STATUS hdmi_drv_init(void) { enum HDMI_VIDEO_RESOLUTION vfmt; HDMI_FUNC(); vfmt = hdmi_params->init_config.vformat; p->hdmi_width = hdmi_resolution_param_table[vfmt][0]; p->hdmi_height = hdmi_resolution_param_table[vfmt][1]; p->bg_width = 0; p->bg_height = 0; p->output_video_resolution = vfmt; p->output_audio_format = hdmi_params->init_config.aformat; p->scaling_factor = hdmi_params->scaling_factor < 10 ? hdmi_params->scaling_factor : 10; p->is_clock_on = false; /* <--Donglei */ if (!hdmi_fence_release_task) { disp_register_module_irq_callback(DISP_MODULE_RDMA, _hdmi_rdma_irq_handler); hdmi_fence_release_task = kthread_create(hdmi_fence_release_kthread, NULL, "hdmi_fence_release_kthread"); wake_up_process(hdmi_fence_release_task); } if (!hdmi_3d_config_task) { hdmi_3d_config_task = kthread_create(hdmi_3d_config_kthread, NULL, "hdmi_3d_config_kthread"); wake_up_process(hdmi_3d_config_task); } #if defined(CONFIG_MTK_DCS) if (!dcs_switch_to_4ch_task) { dcs_switch_to_4ch_task = kthread_create(hdmi_dcs_switch_to_4ch_kthread, NULL, "hdmi_dcs_switch_to_4ch_kthread"); wake_up_process(dcs_switch_to_4ch_task); } #endif return HDMI_STATUS_OK; } /* Release memory */ /* Will only be used in ioctl(MTK_HDMI_AUDIO_VIDEO_ENABLE) */ static enum HDMI_STATUS hdmi_drv_deinit(void) { HDMI_FUNC(); hdmi_free_hdmi_buffer(); return HDMI_STATUS_OK; } /* Reset HDMI Driver state */ static void hdmi_state_reset(void) { HDMI_FUNC(); if (hdmi_drv->get_state() == HDMI_STATE_ACTIVE) { if (enable_ut != 1) switch_set_state(&hdmi_switch_data, HDMI_STATE_ACTIVE); hdmi_enable_dvfs(true); hdmi_reschange = HDMI_VIDEO_RESOLUTION_NUM; } else { if (enable_ut != 1) { switch_set_state(&hdmi_switch_data, HDMI_STATE_NO_DEVICE); switch_set_state(&hdmires_switch_data, 0); } hdmi_enable_dvfs(false); } } /* static */ void hdmi_suspend(void) { int session_id = 0; HDMI_FUNC(); if (IS_HDMI_NOT_ON()) return; mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_START, Plugout, 0); if (down_interruptible(&hdmi_update_mutex)) { HDMI_ERR("[hdmi][HDMI] can't get semaphore in %s()\n", __func__); return; } SET_HDMI_STANDBY(); hdmi_drv->suspend(); p->is_mhl_video_on = false; p->is_clock_on = false; ext_disp_suspend(MHL_SESSION_ID); session_id = ext_disp_get_sess_id(); #if defined(CONFIG_MTK_DCS) /* Nortify DCS can switch to 2ch */ dcs_exit_perf(DCS_KICKER_MHL); #endif first_frame_done = 0; rdmafpscnt = 0; up(&hdmi_update_mutex); mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_END, Plugout, 0); } /* static */ void hdmi_resume(void) { HDMI_LOG("p->state is %d,(0:off, 1:on, 2:standby)\n", atomic_read(&p->state)); if (IS_HDMI_NOT_STANDBY()) return; mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_START, Plugin, 0); if (down_interruptible(&hdmi_update_mutex)) { HDMI_ERR("[hdmi][HDMI] can't get semaphore in %s()\n", __func__); return; } SET_HDMI_ON(); p->is_clock_on = true; hdmi_drv->resume(); up(&hdmi_update_mutex); mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_END, Plugin, 0); } void hdmi_power_on(void) { HDMI_FUNC(); if (IS_HDMI_NOT_OFF()) return; if (down_interruptible(&hdmi_update_mutex)) { HDMI_ERR("[hdmi][HDMI] can't get semaphore in %s()\n", __func__); return; } SET_HDMI_STANDBY(); hdmi_drv->power_on(); ext_disp_resume(MHL_SESSION_ID); up(&hdmi_update_mutex); if (p->is_force_disable == false) { if (IS_HDMI_FAKE_PLUG_IN()) { hdmi_resume(); msleep(1000); switch_set_state(&hdmi_switch_data, HDMI_STATE_ACTIVE); hdmi_reschange = HDMI_VIDEO_RESOLUTION_NUM; } else { /* this is just a ugly workaround for some tv sets.. */ if (hdmi_drv->get_state() == HDMI_STATE_ACTIVE) { hdmi_drv->get_params(hdmi_params); hdmi_resume(); } hdmi_state_reset(); } } } void hdmi_power_off(void) { HDMI_FUNC(); if (IS_HDMI_OFF()) return; if (down_interruptible(&hdmi_update_mutex)) { HDMI_ERR("[hdmi][HDMI] can't get semaphore in %s()\n", __func__); return; } hdmi_drv->power_off(); ext_disp_suspend(MHL_SESSION_ID); p->is_clock_on = false; SET_HDMI_OFF(); up(&hdmi_update_mutex); switch_set_state(&hdmires_switch_data, 0); hdmi_enable_dvfs(false); #if defined(CONFIG_MTK_DCS) /* Nortify DCS can switch to 2ch */ dcs_exit_perf(DCS_KICKER_MHL); #endif } static void hdmi_resolution_setting(int arg) { HDMI_FUNC(); extd_dpi_params.dispif_config.dpi.dsc_enable = 0; if (hdmi_drv && hdmi_drv->get_params) { hdmi_params->init_config.vformat = arg; hdmi_drv->get_params(hdmi_params); if (dst_is_dsi) { struct LCM_DSC_CONFIG_PARAMS *pdsc; pdsc = &extd_dpi_params.dispif_config.dsi.dsc_params; memcpy(&extd_dpi_params.dispif_config.dsi, (void *)(&hdmi_params->dsi_params), sizeof(struct LCM_DSI_PARAMS)); p->bg_height = ((hdmi_params->height * p->scaling_factor) / 100 >> 2) << 2; p->bg_width = ((hdmi_params->width * p->scaling_factor) / 100 >> 2) << 2; p->hdmi_width = hdmi_params->width - p->bg_width; p->hdmi_height = hdmi_params->height - p->bg_height; extd_dpi_params.dispif_config.width = p->hdmi_width; extd_dpi_params.dispif_config.height = p->hdmi_height; extd_dpi_params.dispif_config.type = LCM_TYPE_DSI; p->output_video_resolution = hdmi_params->init_config.vformat; if ((arg == HDMI_VIDEO_2160p_DSC_24Hz) || (arg == HDMI_VIDEO_2160p_DSC_30Hz)) { memset(pdsc, 0, sizeof(*pdsc)); extd_dpi_params.dispif_config.dsi.dsc_enable = 1; /* width/(slice_mode's slice) */ pdsc->slice_width = 1920; pdsc->slice_hight = 8; /* 128: 1/3 compress; 192: 1/2 compress */ pdsc->bit_per_pixel = 128; /* 0: 1 slice; 1: 2 slice; 2: 3 slice */ pdsc->slice_mode = 1; pdsc->rgb_swap = 0; pdsc->xmit_delay = 0x200; pdsc->dec_delay = 0x4c0; pdsc->scale_value = 0x20; pdsc->increment_interval = 0x11e; pdsc->decrement_interval = 0x1a; pdsc->nfl_bpg_offset = 0xdb7; pdsc->slice_bpg_offset = 0x394; pdsc->final_offset = 0x10f0; pdsc->line_bpg_offset = 0xc; pdsc->bp_enable = 0x0; pdsc->rct_on = 0x0; } } else { struct LCM_DPI_PARAMS *dpi; struct LCM_DSC_CONFIG_PARAMS *pdsc; dpi = &extd_dpi_params.dispif_config.dpi; pdsc = &extd_dpi_params.dispif_config.dpi.dsc_params; dpi->clk_pol = hdmi_params->clk_pol; dpi->de_pol = hdmi_params->de_pol; dpi->hsync_pol = hdmi_params->hsync_pol; dpi->vsync_pol = hdmi_params->vsync_pol; dpi->hsync_pulse_width = hdmi_params->hsync_pulse_width; dpi->hsync_back_porch = hdmi_params->hsync_back_porch; dpi->hsync_front_porch = hdmi_params->hsync_front_porch; dpi->vsync_pulse_width = hdmi_params->vsync_pulse_width; dpi->vsync_back_porch = hdmi_params->vsync_back_porch; dpi->vsync_front_porch = hdmi_params->vsync_front_porch; dpi->dpi_clock = hdmi_params->input_clock; p->bg_height = ((hdmi_params->height * p->scaling_factor) / 100 >> 2) << 2; p->bg_width = ((hdmi_params->width * p->scaling_factor) / 100 >> 2) << 2; p->hdmi_width = hdmi_params->width - p->bg_width; p->hdmi_height = hdmi_params->height - p->bg_height; p->output_video_resolution = hdmi_params->init_config.vformat; dpi->width = p->hdmi_width; dpi->height = p->hdmi_height; if ((arg == HDMI_VIDEO_2160p_DSC_24Hz) || (arg == HDMI_VIDEO_2160p_DSC_30Hz)) { memset(pdsc, 0, sizeof(*pdsc)); dpi->dsc_enable = 1; dpi->width = p->hdmi_width / 3; /* width/(slice_mode's slice) */ pdsc->slice_width = 1920; pdsc->slice_hight = 8; /* 128: 1/3 compress; 192: 1/2 compress */ pdsc->bit_per_pixel = 128; /* 0: 1 slice; 1: 2 slice; 2: 3 slice */ pdsc->slice_mode = 1; pdsc->rgb_swap = 0; pdsc->xmit_delay = 0x200; pdsc->dec_delay = 0x4c0; pdsc->scale_value = 0x20; pdsc->increment_interval = 0x11e; pdsc->decrement_interval = 0x1a; pdsc->nfl_bpg_offset = 0xdb7; pdsc->slice_bpg_offset = 0x394; pdsc->final_offset = 0x10f0; pdsc->line_bpg_offset = 0xc; pdsc->bp_enable = 0x0; pdsc->rct_on = 0x0; } dpi->bg_width = p->bg_width; dpi->bg_height = p->bg_height; dpi->format = LCM_DPI_FORMAT_RGB888; dpi->rgb_order = LCM_COLOR_ORDER_RGB; dpi->i2x_en = true; dpi->i2x_edge = 2; dpi->embsync = false; } } ext_disp_set_lcm_param(&(extd_dpi_params.dispif_config)); HDMI_LOG("hdmi_resolution_setting_res (%d)\n", arg); } int hdmi_check_resolution(int src_w, int src_h, int physical_w, int physical_h) { int ret = 0; if (physical_w <= 0 || physical_h <= 0 || src_w > physical_w || src_h > physical_h) { HDMI_LOG("%s fail\n", __func__); ret = -1; } return ret; } int hdmi_recompute_bg(int src_w, int src_h) { int ret = 0; return ret; } /* HDMI Driver state callback function */ void hdmi_state_callback(enum HDMI_STATE state) { HDMI_LOG("[hdmi]%s, state = %d\n", __func__, state); if ((p->is_force_disable == true) || (IS_HDMI_FAKE_PLUG_IN())) return; switch (state) { case HDMI_STATE_NO_DEVICE: { #ifdef MHL_DYNAMIC_VSYNC_OFFSET ged_dvfs_vsync_offset_event_switch( GED_DVFS_VSYNC_OFFSET_MHL_EVENT, false); #endif hdmi_suspend(); switch_set_state(&hdmi_switch_data, HDMI_STATE_NO_DEVICE); switch_set_state(&hdmires_switch_data, 0); hdmi_enable_dvfs(false); #if defined(CONFIG_MTK_SMARTBOOK_SUPPORT) && defined(CONFIG_HAS_SBSUSPEND) if (hdmi_params->cabletype == MHL_SMB_CABLE) sb_plug_out(); #endif HDMI_LOG("[hdmi]%s, state = %d out!\n", __func__, state); break; } case HDMI_STATE_ACTIVE: { if (IS_HDMI_ON()) { HDMI_LOG("[hdmi]%s, already on(%d) !\n", __func__, atomic_read(&p->state)); break; } hdmi_drv->get_params(hdmi_params); hdmi_resume(); if (atomic_read(&p->state) > HDMI_POWER_STATE_OFF) { hdmi_reschange = HDMI_VIDEO_RESOLUTION_NUM; switch_set_state(&hdmi_switch_data, HDMI_STATE_ACTIVE); hdmi_enable_dvfs(true); } #if defined(CONFIG_MTK_SMARTBOOK_SUPPORT) && defined(CONFIG_HAS_SBSUSPEND) if (hdmi_params->cabletype == MHL_SMB_CABLE) sb_plug_in(); #endif #ifdef MHL_DYNAMIC_VSYNC_OFFSET ged_dvfs_vsync_offset_event_switch( GED_DVFS_VSYNC_OFFSET_MHL_EVENT, true); #endif HDMI_LOG("[hdmi]%s, state = %d out!\n", __func__, state); break; } default: { HDMI_LOG("[hdmi]%s, state not support\n", __func__); break; } } } void hdmi_set_layer_num(int layer_num) { if (layer_num >= 0) #ifdef FIX_EXTD_TO_OVL_PATH hdmi_layer_num = FIX_EXTD_TO_OVL_PATH; #else hdmi_layer_num = layer_num; #endif } int hdmi_enable(int enable) { HDMI_FUNC(); if (enable) { if (p->is_enabled) { HDMI_LOG("[hdmi] hdmi already enable, %s()\n", __func__); return 0; } if (hdmi_drv->enter) hdmi_drv->enter(); hdmi_drv_init(); hdmi_power_on(); p->is_enabled = true; } else { if (!p->is_enabled) return 0; hdmi_power_off(); hdmi_drv_deinit(); /* when disable hdmi, HPD is disabled */ switch_set_state(&hdmi_switch_data, HDMI_STATE_NO_DEVICE); p->is_enabled = false; if (hdmi_drv->exit) hdmi_drv->exit(); } return 0; } int hdmi_power_enable(int enable) { HDMI_FUNC(); if (!p->is_enabled) { HDMI_LOG("return in %d\n", __LINE__); return 0; } if (enable) { if (otg_enable_status) { HDMI_LOG("return in %d\n", __LINE__); return 0; } hdmi_power_on(); } else { hdmi_power_off(); switch_set_state(&hdmi_switch_data, HDMI_STATE_NO_DEVICE); } return 0; } void hdmi_force_disable(int enable) { HDMI_FUNC(); if (!p->is_enabled) { HDMI_LOG("return in %d\n", __LINE__); return; } if (IS_HDMI_OFF()) { HDMI_LOG("return in %d\n", __LINE__); return; } if (enable) { if (p->is_force_disable == true) return; if (IS_HDMI_FAKE_PLUG_IN() || (hdmi_drv->get_state() == HDMI_STATE_ACTIVE)) { hdmi_suspend(); switch_set_state(&hdmi_switch_data, HDMI_STATE_NO_DEVICE); switch_set_state(&hdmires_switch_data, 0); } p->is_force_disable = true; } else { if (p->is_force_disable == false) return; if (IS_HDMI_FAKE_PLUG_IN() || (hdmi_drv->get_state() == HDMI_STATE_ACTIVE)) { hdmi_resume(); msleep(1000); switch_set_state(&hdmi_switch_data, HDMI_STATE_ACTIVE); hdmi_reschange = HDMI_VIDEO_RESOLUTION_NUM; } p->is_force_disable = false; } } void hdmi_set_USBOTG_status(int status) { HDMI_LOG("MTK_HDMI_USBOTG_STATUS, arg=%d, enable %d\n", status, p->is_enabled); if (!p->is_enabled) { HDMI_LOG("return in %d\n", __LINE__); return; } if (hdmi_params->cabletype != MHL_CABLE) { HDMI_LOG("return in %d\n", __LINE__); return; } if (status) { otg_enable_status = true; } else { otg_enable_status = false; if (p->is_force_disable) { HDMI_LOG("return in %d\n", __LINE__); return; } hdmi_power_on(); } } int hdmi_set_audio_enable(int enable) { if (!p->is_enabled) { HDMI_LOG("return in %d\n", __LINE__); return 0; } hdmi_drv->audio_enable(enable); return 0; } void hdmi_set_video_enable(int enable) { if (!p->is_enabled) { HDMI_LOG("return in %d\n", __LINE__); return; } hdmi_drv->video_enable(enable); } int hdmi_set_resolution(int res) { int extd_path_state = 0; int i = 0; int session_id = 0; HDMI_LOG("video res config, res:%d, old res:%ld, video_on:%d\n", res, hdmi_reschange, p->is_mhl_video_on); if (!p->is_enabled) { HDMI_LOG("return in %d\n", __LINE__); return 0; } /* just for debug */ if ((force_reschange & 0xff) < 0xff) res = force_reschange & 0xff; if (hdmi_reschange == res) { HDMI_LOG("hdmi_reschange=%ld\n", hdmi_reschange); return 0; } #if defined(CONFIG_MTK_DCS) if (res == HDMI_VIDEO_2160p_DSC_30Hz || res == HDMI_VIDEO_2160p_DSC_24Hz) { /* Notify the DCS switch to 4ch */ dcs_enter_perf(DCS_KICKER_MHL); atomic_set(&dcs_4ch_switch_event, 1); HDMI_LOG("Notify the DCS switch to 4ch\n"); /* wake up dcs_switch_to_4ch_kthread for wait switch done */ wake_up_interruptible(&dcs_switch_to_4ch_wq); } #endif p->is_clock_on = false; hdmi_reschange = res; mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_START, ResChange, res); if (down_interruptible(&hdmi_update_mutex)) { HDMI_LOG("[HDMI] can't get semaphore in\n"); return 0; } extd_path_state = ext_disp_is_alive(); if (extd_path_state == EXTD_RESUME) { if (hdmi_drv && hdmi_drv->suspend) hdmi_drv->suspend(); ext_disp_suspend(MHL_SESSION_ID); session_id = ext_disp_get_sess_id(); for (i = 0; i < EXTD_OVERLAY_CNT; i++) mtkfb_release_layer_fence(session_id, i); } hdmi_resolution_setting(res); p->is_mhl_video_on = false; first_frame_done = 0; rdmafpscnt = 0; up(&hdmi_update_mutex); if (enable_ut != 1) switch_set_state(&hdmires_switch_data, hdmi_reschange + 1); p->is_clock_on = true; mmprofile_log_ex(ddp_mmp_get_events()->Extd_State, MMPROFILE_FLAG_END, ResChange, hdmi_reschange + 1); return 0; } int hdmi_get_dev_info(int is_sf, void *info) { int ret = 0; if (is_sf == AP_GET_INFO) { int displayid = 0; struct mtk_dispif_info hdmi_info; if (!info) { HDMI_LOG("ioctl pointer is NULL\n"); return -EFAULT; } mmprofile_log_ex(ddp_mmp_get_events()->Extd_DevInfo, MMPROFILE_FLAG_START, p->is_enabled, p->is_clock_on); if (copy_from_user(&displayid, info, sizeof(displayid))) { mmprofile_log_ex(ddp_mmp_get_events()->Extd_ErrorInfo, MMPROFILE_FLAG_PULSE, Devinfo, 0); HDMI_ERR(": copy_from_user failed! line:%d\n", __LINE__); return -EAGAIN; } if (displayid != MTKFB_DISPIF_HDMI) HDMI_LOG(": invalid display id:%d\n", displayid); memset(&hdmi_info, 0, sizeof(hdmi_info)); hdmi_info.displayFormat = DISPIF_FORMAT_RGB888; hdmi_info.displayHeight = p->hdmi_height; hdmi_info.displayWidth = p->hdmi_width; hdmi_info.display_id = displayid; hdmi_info.isConnected = 1; hdmi_info.displayMode = DISPIF_MODE_COMMAND; if (hdmi_params->cabletype == MHL_SMB_CABLE) hdmi_info.displayType = HDMI_SMARTBOOK; else if (hdmi_params->cabletype == MHL_CABLE) hdmi_info.displayType = MHL; else if (hdmi_params->cabletype == SLIMPORT_CABLE) hdmi_info.displayType = SLIMPORT; else hdmi_info.displayType = HDMI; hdmi_info.isHwVsyncAvailable = HW_DPI_VSYNC_SUPPORT; if ((hdmi_reschange == HDMI_VIDEO_1920x1080p_30Hz) || (hdmi_reschange == HDMI_VIDEO_2160p_DSC_30Hz)) hdmi_info.vsyncFPS = 3000; else if (hdmi_reschange == HDMI_VIDEO_2160p_DSC_24Hz) hdmi_info.vsyncFPS = 2400; else hdmi_info.vsyncFPS = 6000; if (copy_to_user(info, &hdmi_info, sizeof(hdmi_info))) { mmprofile_log_ex(ddp_mmp_get_events()->Extd_ErrorInfo, MMPROFILE_FLAG_PULSE, Devinfo, 1); HDMI_ERR("copy_to_user failed! line:%d\n", __LINE__); ret = -EFAULT; } mmprofile_log_ex(ddp_mmp_get_events()->Extd_DevInfo, MMPROFILE_FLAG_END, p->is_enabled, hdmi_info.displayType); HDMI_LOG("DEV_INFO configuration get displayType-%d\n", hdmi_info.displayType); } else if (is_sf == SF_GET_INFO) { struct disp_session_info *dispif_info = NULL; dispif_info = (struct disp_session_info *)info; memset((void *)dispif_info, 0, sizeof(*dispif_info)); dispif_info->isOVLDisabled = (hdmi_layer_num == 1) ? 1 : 0; dispif_info->maxLayerNum = hdmi_layer_num; dispif_info->displayFormat = DISPIF_FORMAT_RGB888; dispif_info->displayHeight = p->hdmi_height; dispif_info->displayWidth = p->hdmi_width; dispif_info->displayMode = DISP_IF_MODE_VIDEO; if (hdmi_params->cabletype == MHL_SMB_CABLE) { dispif_info->displayType = DISP_IF_HDMI_SMARTBOOK; if (IS_HDMI_OFF()) dispif_info->displayType = DISP_IF_MHL; } else if (hdmi_params->cabletype == MHL_CABLE) dispif_info->displayType = DISP_IF_MHL; else if (hdmi_params->cabletype == SLIMPORT_CABLE) dispif_info->displayType = DISP_IF_SLIMPORT; else dispif_info->displayType = DISP_IF_HDMI; dispif_info->isHwVsyncAvailable = HW_DPI_VSYNC_SUPPORT; if ((hdmi_reschange == HDMI_VIDEO_1920x1080p_30Hz) || (hdmi_reschange == HDMI_VIDEO_2160p_DSC_30Hz)) dispif_info->vsyncFPS = 3000; else if (hdmi_reschange == HDMI_VIDEO_2160p_DSC_24Hz) dispif_info->vsyncFPS = 2400; else dispif_info->vsyncFPS = 6000; if (dispif_info->displayWidth * dispif_info->displayHeight <= 240 * 432) dispif_info->physicalHeight = dispif_info->physicalWidth = 0; else if (dispif_info->displayWidth * dispif_info->displayHeight <= 320 * 480) dispif_info->physicalHeight = dispif_info->physicalWidth = 0; else if (dispif_info->displayWidth * dispif_info->displayHeight <= 480 * 854) dispif_info->physicalHeight = dispif_info->physicalWidth = 0; else dispif_info->physicalHeight = dispif_info->physicalWidth = 0; dispif_info->isConnected = 1; dispif_info->isHDCPSupported = hdmi_params->HDCPSupported; /* fake 3d assert for debug */ if ((force_reschange > 0xff) && (force_reschange < 0xffff)) hdmi_params->is_3d_support = 1; dispif_info->is3DSupport = hdmi_params->is_3d_support; } return ret; } int hdmi_get_capability(void *info) { int ret = 0; int query_type = 0; query_type = hdmi_get_support_info(); if (copy_to_user(info, &query_type, sizeof(query_type))) { HDMI_LOG(": copy_to_user error! line:%d\n", __LINE__); ret = -EFAULT; } HDMI_LOG("[hdmi][HDMI] query_type done %x\n", query_type); return ret; } int hdmi_get_edid(void *edid_info) { int ret = 0; struct _HDMI_EDID_T pv_get_info; memset(&pv_get_info, 0, sizeof(pv_get_info)); if (!edid_info) { HDMI_LOG("ioctl pointer is NULL\n"); return -EFAULT; } if (hdmi_drv->getedid) { hdmi_drv->getedid(&pv_get_info); #ifdef MHL_RESOLUTION_LIMIT_720P_60 pv_get_info.ui4_pal_resolution &= (~SINK_1080P60); pv_get_info.ui4_pal_resolution &= (~SINK_1080P30); #endif #ifdef MHL_RESOLUTION_LIMIT_1080P_30 if (pv_get_info.ui4_pal_resolution & SINK_1080P60) pv_get_info.ui4_pal_resolution &= (~SINK_1080P60); pv_get_info.ui4_pal_resolution &= (~SINK_2160p30); pv_get_info.ui4_pal_resolution &= (~SINK_2160p24); #endif if (pv_get_info.ui4_pal_resolution & SINK_1080P60) pv_get_info.ui4_pal_resolution &= (~SINK_1080P30); } if (copy_to_user(edid_info, &pv_get_info, sizeof(pv_get_info))) { HDMI_LOG("copy_to_user failed! line:%d\n", __LINE__); ret = -EFAULT; } return ret; } int hdmi_get_device_type(void) { int device_type = -1; if (IS_HDMI_ON()) { if (hdmi_params->cabletype == MHL_SMB_CABLE) device_type = DISP_IF_HDMI_SMARTBOOK; else if (hdmi_params->cabletype == MHL_CABLE) device_type = DISP_IF_MHL; else if (hdmi_params->cabletype == SLIMPORT_CABLE) device_type = DISP_IF_SLIMPORT; } return device_type; } int hdmi_ioctl(unsigned int ioctl_cmd, int param1, int param2, unsigned long *params) { int ret = 0; /* HDMI_LOG("%s ioctl_cmd:%d\n", __func__, ioctl_cmd); */ switch (ioctl_cmd) { case RECOMPUTE_BG_CMD: ret = hdmi_recompute_bg(param1, param2); break; case GET_DEV_TYPE_CMD: ret = hdmi_get_device_type(); break; case SET_LAYER_NUM_CMD: hdmi_set_layer_num(param1); break; default: HDMI_LOG("%s unknown command\n", __func__); break; } return ret; } void hdmi_udelay(unsigned int us) { udelay(us); } void hdmi_mdelay(unsigned int ms) { msleep(ms); } int hdmi_init(void) { int ret = 0; struct device_node *node; const char interface_type[10]; const char *type = interface_type; HDMI_LOG("%s start\n", __func__); /* for support hdmi hotplug, inform AP the event */ hdmi_switch_data.name = "hdmi"; hdmi_switch_data.index = 0; hdmi_switch_data.state = HDMI_STATE_NO_DEVICE; ret = switch_dev_register(&hdmi_switch_data); if (ret) HDMI_ERR("[hdmi][HDMI]switch_dev_register failed, return:%d!\n", ret); hdmires_switch_data.name = "res_hdmi"; hdmires_switch_data.index = 0; hdmires_switch_data.state = 0; ret = switch_dev_register(&hdmires_switch_data); if (ret) HDMI_ERR("[hdmi][HDMI]switch_dev_register failed, return:%d!\n", ret); node = of_find_compatible_node(NULL, NULL, "mediatek,extd_dev"); if (!node) EXT_MGR_ERR("Failed to find device node mediatek,extd_dev\n"); else of_property_read_string(node, "interface_type", &type); EXT_MGR_LOG("interface_type is %s\n", type); if (!strncmp(type, "DPI", 3)) { EXT_MGR_LOG("interface_type is DPI\n"); dst_is_dsi = 0; } else if (!strncmp(type, "DSI", 3)) { EXT_MGR_LOG("interface_type is DSI\n"); dst_is_dsi = 1; } return 0; } int hdmi_post_init(void) { int boot_mode = 0; const struct EXTD_DRIVER *extd_factory_driver = NULL; static const struct EXTERNAL_DISPLAY_UTIL_FUNCS extd_utils = { .hdmi_video_format_config = hdmi_video_format_config, }; static const struct HDMI_UTIL_FUNCS hdmi_utils = { .udelay = hdmi_udelay, .mdelay = hdmi_mdelay, .state_callback = hdmi_state_callback, }; hdmi_drv = (struct HDMI_DRIVER *)HDMI_GetDriver(); if (!hdmi_drv) { HDMI_ERR("[hdmi]%s, hdmi_init fail\n", __func__); return -1; } hdmi_drv->set_util_funcs(&hdmi_utils); extd_disp_drv_set_util_funcs(&extd_utils); hdmi_params->init_config.vformat = HDMI_VIDEO_1280x720p_60Hz; hdmi_drv->get_params(hdmi_params); hdmi_drv->init(); if (hdmi_drv->register_callback) { boot_mode = (int)get_boot_mode(); if (boot_mode == FACTORY_BOOT || boot_mode == ATE_FACTORY_BOOT) { extd_factory_driver = EXTD_Factory_HDMI_Driver(); if (extd_factory_driver) extd_factory_driver->init(); } else { hdmi_drv->register_callback(hdmi_state_callback); } } memset((void *)&hdmi_context, 0, sizeof(hdmi_context)); memset((void *)&extd_dpi_params, 0, sizeof(extd_dpi_params)); p->output_mode = hdmi_params->output_mode; SET_HDMI_OFF(); init_waitqueue_head(&hdmi_fence_release_wq); init_waitqueue_head(&hdmi_vsync_wq); init_waitqueue_head(&hdmi_3d_config_wq); #if defined(CONFIG_MTK_DCS) init_waitqueue_head(&dcs_switch_to_4ch_wq); init_waitqueue_head(&hdmi_video_config_wq); #endif extd_dbg_init(); return 0; } #endif /* CONFIG_MTK_HDMI_SUPPORT */ const struct EXTD_DRIVER *EXTD_HDMI_Driver(void) { static const struct EXTD_DRIVER extd_driver_hdmi = { #if defined(CONFIG_MTK_HDMI_SUPPORT) .init = hdmi_init, .post_init = hdmi_post_init, .deinit = NULL, .enable = hdmi_enable, .power_enable = hdmi_power_enable, .set_audio_enable = hdmi_set_audio_enable, .set_audio_format = hdmi_audio_config, .set_resolution = hdmi_set_resolution, .get_dev_info = hdmi_get_dev_info, .get_capability = hdmi_get_capability, .get_edid = hdmi_get_edid, .wait_vsync = hdmi_waitVsync, .fake_connect = hdmi_cable_fake_connect, .factory_mode_test = NULL, .ioctl = hdmi_ioctl, #else .init = 0, #endif }; return &extd_driver_hdmi; }