#include "drv_types.h" #include "sysreg.h" #include "hdmi.h" #include "hdmi_hw.h" #include "hdmi_hpd.h" #include "hdmi_dbg.h" #include "hdmi_processing.h" #include "hdmi_notice.h" #include "hdmi_switch.h" #include "hdmi_cfg.h" #include "cec.h" #include "debounce.h" #include "mhl/cbus_drv.h" #include "hdmi_time.h" //for ARC GPIO 5V #include "drv_gpio.h" extern GPIOPin_t geGpio5V_Pin; #ifndef HDMI_DDC5V_WORKAROUND static DEBOUNCE hpd_db; #endif static HDMI_SRC_T cur_src = HDMI_SRC_NULL; static HDMI_PORT_T cur_port = HDMI_PORT_NULL; static SWITCH_PORT_T cur_sw_port = SWITCH_PORT_NULL; static struct timer_list term_timer; static BOOL IsHDMISelected(void) { return cur_port != HDMI_PORT_NULL; } static BOOL IsHWPortSelected(void) { return IsHDMISelected() && (cur_sw_port == SWITCH_PORT_NULL); } void hdmi_set_termination(DRV_HDMI_PORT_e ePort, DRV_HPD_LEVEL_e eLevel) { UINT8 bCurTerm; bCurTerm = HDMI_RegisterRead(HDMIRX_PHY_RTT_EN_P_2_0_); if(eLevel == DRV_HPD_LEVEL_LOW) bCurTerm = bCurTerm & (~ePort); else if(eLevel == DRV_HPD_LEVEL_HIGH) bCurTerm = bCurTerm | ePort; HDMI_RegisterWrite(HDMIRX_PHY_RTT_EN_P_2_0_, bCurTerm); } static void set_reset_phy(void) { if (IsHWPortSelected() && hdmi_get_hpd_at_cur_src()) { //UINT8 b = 0; HDMI_RegisterWrite(HDMIRX_R_rst_n, 0); HDMI_RegisterWrite(HDMIRX_R_rst_n, 1); /* Clear phy settings */ //PD for CEC HDMI_PHY_Enable(FALSE); /* Wait */ HDMI_DelayMs(10); //Init Phy for HDMI 75M port A HDMI_PHY_Enable(TRUE); } } static UINT32 hpd_status = 0x0; static UINT32 switch_hpd_status = 0x0; static UINT32 hdmi_status = 0; static UINT32 hpd_mask_hw = 0x0; static UINT32 hpd_mask_sw = 0x0; static BOOL bToggle = false; extern BOOL MHL_CABLE_IN; static BOOL bfToggle = false; static void hdmi_repot_hpd_status(void) { HDMI_SRC_T src; SWITCH_PORT_T sw_port; HDMI_PORT_T hw_port; UINT32 local_hdmi_status = 0; UINT32 change_hdmi_status = 0; for (src = HDMI_SRC_1; src < 8; src++) { UINT32 s = 0; sw_port = hdmi_sw_port(src); hw_port = hdmi_hw_port(src); if (sw_port == SWITCH_PORT_NULL) { /* Current HDMI source is a hw port */ if (hw_port != HDMI_PORT_NULL) { s = ((hpd_status & (1 << hw_port)) ? 1 : 0); } } else { /* Current HDMI source is a switch port */ s = ((switch_hpd_status & (1 << sw_port)) ? 1 : 0); } local_hdmi_status |= (s << src); } change_hdmi_status = local_hdmi_status ^ hdmi_status; hdmi_status = local_hdmi_status; /* Notice Flow Control the current hpd status */ HDMI_NoticeHotPlug(((hdmi_status << 8) & 0xff00) | (change_hdmi_status & 0xff)); hdmidbg("%s change_hdmi_status:%x hdmi_status:%x cur_port%x\n", __FUNCTION__,change_hdmi_status,hdmi_status,cur_port); } void hdmi_report_cur_hpd(void) { //if (hdmi_status) { /* Report current hpd status again */ HDMI_NoticeHotPlug(((hdmi_status << 8) & 0xff00) | (hdmi_status & 0xff)); } } BOOL hdmi_get_hpd_at_cur_src(void) { if (cur_sw_port == SWITCH_PORT_NULL) { /* Current hdmi channel is a hw port */ return (hpd_status & (1 << cur_port)) ? true : false; } else if (cur_port != HDMI_PORT_NULL) { /* Current hdmi channel is a sw port */ //return (switch_hpd_status & (1 << cur_sw_port)) ? true : false; return ((hdmi_switch_hpd_status() & hpd_mask_sw) & (1 << cur_sw_port)) ? true : false; } return false; } void hdmi_apply_hpd(DRV_HDMI_PORT_e ePort, DRV_HPD_LEVEL_e eLevel) { volatile CUSTIMIZATION_TABLEPTR pCst; #ifndef HDMI_DDC5V_WORKAROUND HDMI_PORT_T eArcPort = DRV_Get_GPIO5V_ARCPort(); #endif pCst = (volatile CUSTIMIZATION_TABLEPTR)SPI_OPTIONDATA_SHADOWADDR; if (bfToggle == TRUE) { return; } hdmidbg("set HPD port:%d level:%d\n", ePort, eLevel); if(ePort & pCst->HDMIHPDInvertMap) { if(eLevel == DRV_HPD_LEVEL_HIGH) { eLevel = DRV_HPD_LEVEL_LOW; } else { eLevel = DRV_HPD_LEVEL_HIGH; } } #ifdef HDMI_DDC5V_WORKAROUND if(ePort & DRV_HDMI_PORT_A) { HDMI_RegisterWrite(CEC_R_hpd_val_0, eLevel); } if(ePort & DRV_HDMI_PORT_B) { HDMI_RegisterWrite(CEC_R_hpd_val_1, eLevel); } if(ePort & DRV_HDMI_PORT_C) { HDMI_RegisterWrite(CEC_R_hpd_val_2, eLevel); } #else if(ePort & DRV_HDMI_PORT_A) { if(eArcPort != HDMI_PORT_A) HDMI_RegisterWrite(CEC_R_hpd_val_0, HDMI_RegisterRead(CEC_ddc5v_0) ? eLevel : DRV_HPD_LEVEL_LOW); else HDMI_RegisterWrite(CEC_R_hpd_val_0, GPIOTryRead(geGpio5V_Pin) ? eLevel : DRV_HPD_LEVEL_LOW); } if(ePort & DRV_HDMI_PORT_B) { if(eArcPort != HDMI_PORT_B) HDMI_RegisterWrite(CEC_R_hpd_val_1, HDMI_RegisterRead(CEC_ddc5v_1) ? eLevel : DRV_HPD_LEVEL_LOW); else HDMI_RegisterWrite(CEC_R_hpd_val_1, GPIOTryRead(geGpio5V_Pin) ? eLevel : DRV_HPD_LEVEL_LOW); } if(ePort & DRV_HDMI_PORT_C) { if(eArcPort != HDMI_PORT_C) HDMI_RegisterWrite(CEC_R_hpd_val_2, HDMI_RegisterRead(CEC_ddc5v_2) ? eLevel : DRV_HPD_LEVEL_LOW); else HDMI_RegisterWrite(CEC_R_hpd_val_2, GPIOTryRead(geGpio5V_Pin) ? eLevel : DRV_HPD_LEVEL_LOW); } #endif hdmidbg("Port HPD A:%d B:%d C:%d(invert:%d)\n", HDMI_RegisterRead(CEC_R_hpd_val_0), HDMI_RegisterRead(CEC_R_hpd_val_1), HDMI_RegisterRead(CEC_R_hpd_val_2), pCst->HDMIHPDInvertMap); } void hdmi_apply_hpd_Toggle(DRV_HDMI_PORT_e ePort, DRV_HPD_LEVEL_e eLevel, BOOL SetToggle) { volatile CUSTIMIZATION_TABLEPTR pCst; #ifndef HDMI_DDC5V_WORKAROUND HDMI_PORT_T eArcPort = DRV_Get_GPIO5V_ARCPort(); #endif pCst = (volatile CUSTIMIZATION_TABLEPTR)SPI_OPTIONDATA_SHADOWADDR; hdmidbg("set HPD port:%d level:%d\n", ePort, eLevel); bfToggle = SetToggle; if(ePort & pCst->HDMIHPDInvertMap) { if(eLevel == DRV_HPD_LEVEL_HIGH) { eLevel = DRV_HPD_LEVEL_LOW; } else { eLevel = DRV_HPD_LEVEL_HIGH; } } #ifdef HDMI_DDC5V_WORKAROUND if(ePort & DRV_HDMI_PORT_A) { HDMI_RegisterWrite(CEC_R_hpd_val_0, eLevel); } if(ePort & DRV_HDMI_PORT_B) { HDMI_RegisterWrite(CEC_R_hpd_val_1, eLevel); } if(ePort & DRV_HDMI_PORT_C) { HDMI_RegisterWrite(CEC_R_hpd_val_2, eLevel); } #else if(ePort & DRV_HDMI_PORT_A) { if(eArcPort != HDMI_PORT_A) HDMI_RegisterWrite(CEC_R_hpd_val_0, HDMI_RegisterRead(CEC_ddc5v_0) ? eLevel : DRV_HPD_LEVEL_LOW); else HDMI_RegisterWrite(CEC_R_hpd_val_0, GPIOTryRead(geGpio5V_Pin) ? eLevel : DRV_HPD_LEVEL_LOW); } if(ePort & DRV_HDMI_PORT_B) { if(eArcPort != HDMI_PORT_B) HDMI_RegisterWrite(CEC_R_hpd_val_1, HDMI_RegisterRead(CEC_ddc5v_1) ? eLevel : DRV_HPD_LEVEL_LOW); else HDMI_RegisterWrite(CEC_R_hpd_val_1, GPIOTryRead(geGpio5V_Pin) ? eLevel : DRV_HPD_LEVEL_LOW); } if(ePort & DRV_HDMI_PORT_C) { if(eArcPort != HDMI_PORT_C) HDMI_RegisterWrite(CEC_R_hpd_val_2, HDMI_RegisterRead(CEC_ddc5v_2) ? eLevel : DRV_HPD_LEVEL_LOW); else HDMI_RegisterWrite(CEC_R_hpd_val_2, GPIOTryRead(geGpio5V_Pin) ? eLevel : DRV_HPD_LEVEL_LOW); } #endif hdmidbg("Port HPD A:%d B:%d C:%d(invert:%d)\n", HDMI_RegisterRead(CEC_R_hpd_val_0), HDMI_RegisterRead(CEC_R_hpd_val_1), HDMI_RegisterRead(CEC_R_hpd_val_2), pCst->HDMIHPDInvertMap); } void hdmi_apply_hpd_by5V(DRV_HDMI_PORT_e ePort) { volatile CUSTIMIZATION_TABLEPTR pCst; //DRV_HPD_LEVEL_e eLevel=DRV_HPD_LEVEL_LOW; DRV_HPD_LEVEL_e ePortA_Level,ePortB_Level,ePortC_Level; HDMI_PORT_T eArcPort = DRV_Get_GPIO5V_ARCPort(); pCst = (volatile CUSTIMIZATION_TABLEPTR)SPI_OPTIONDATA_SHADOWADDR; ePortA_Level = ePortB_Level = ePortC_Level = DRV_HPD_LEVEL_LOW; if(ePort & DRV_HDMI_PORT_A) { if((eArcPort != HDMI_PORT_A)||(geGpio5V_Pin == GPIO_NOT_USE)) { ePortA_Level = HDMI_RegisterRead(CEC_ddc5v_0) ; } else { ePortA_Level = GPIOTryRead(geGpio5V_Pin) ; } } if(ePort & DRV_HDMI_PORT_B) { if((eArcPort != HDMI_PORT_B)||(geGpio5V_Pin == GPIO_NOT_USE)) { ePortB_Level = HDMI_RegisterRead(CEC_ddc5v_1) ; } else { ePortB_Level = GPIOTryRead(geGpio5V_Pin) ; } } if(ePort & DRV_HDMI_PORT_C) { if((eArcPort != HDMI_PORT_C)||(geGpio5V_Pin == GPIO_NOT_USE)) { ePortC_Level = HDMI_RegisterRead(CEC_ddc5v_2) ; } else { ePortC_Level = GPIOTryRead(geGpio5V_Pin) ; } } if(ePort & pCst->HDMIHPDInvertMap) { if(ePortA_Level == DRV_HPD_LEVEL_HIGH) { ePortA_Level = DRV_HPD_LEVEL_LOW; } else { ePortA_Level = DRV_HPD_LEVEL_HIGH; } if(ePortB_Level == DRV_HPD_LEVEL_HIGH) { ePortB_Level = DRV_HPD_LEVEL_LOW; } else { ePortB_Level = DRV_HPD_LEVEL_HIGH; } if(ePortC_Level == DRV_HPD_LEVEL_HIGH) { ePortC_Level = DRV_HPD_LEVEL_LOW; } else { ePortC_Level = DRV_HPD_LEVEL_HIGH; } } if(ePort == DRV_HDMI_PORT_ALL) { hdmidbg("%s set HPD portA level:%d, portB level:%d, portC level:%d\n", __FUNCTION__,ePortA_Level,ePortB_Level,ePortC_Level); } else if(ePort == DRV_HDMI_PORT_A) { hdmidbg("%s set HPD portA level:%d\n",__FUNCTION__, ePortA_Level); } else if(ePort == DRV_HDMI_PORT_B) { hdmidbg("%s set HPD portB level:%d\n",__FUNCTION__, ePortB_Level); } else if(ePort == DRV_HDMI_PORT_C) { hdmidbg("%s set HPD portC level:%d\n",__FUNCTION__, ePortC_Level); } if(ePort & DRV_HDMI_PORT_A) { HDMI_RegisterWrite(CEC_R_hpd_val_0, ePortA_Level); } if(ePort & DRV_HDMI_PORT_B) { HDMI_RegisterWrite(CEC_R_hpd_val_1, ePortB_Level); } if(ePort & DRV_HDMI_PORT_C) { HDMI_RegisterWrite(CEC_R_hpd_val_2, ePortC_Level); } } UINT32 hdmi_get_hpd_status(void) { UINT32 local_hpd_status = 0; local_hpd_status |= (HDMI_RegisterRead(CEC_R_hpd_val_0) & 0x1); local_hpd_status |= ((HDMI_RegisterRead(CEC_R_hpd_val_1) & 0x1) << 1); local_hpd_status |= ((HDMI_RegisterRead(CEC_R_hpd_val_2) & 0x1) << 2); return local_hpd_status; } static void hpd_debounce(void *dummy) { UINT32 local_hpd_status = 0; UINT32 local_switch_hpd_status = 0; UINT32 diff_hpd_status = 0; UINT32 diff_switch_hpd_status = 0; #ifndef CONFIG_CHANGE_HOT_PLUG_ACTION UINT8 fInRange = HDMI_RegisterRead(HDMIRX_IN_RANGE); #endif /* Apply hdmi hpd according to ddc */ #ifdef HDMI_DDC5V_WORKAROUND local_hpd_status = hdmi_get_hpd_status() & hpd_mask_hw; #else hdmi_apply_hpd(DRV_HDMI_PORT_ALL, DRV_HPD_LEVEL_HIGH); local_hpd_status = hdmi_get_hpd_status() & hpd_mask_hw; #endif diff_hpd_status = local_hpd_status ^ hpd_status; hpd_status = local_hpd_status; #ifndef CONFIG_CHANGE_HOT_PLUG_ACTION /* patch: When cable is plugged on current source and current source is not switch port, To pull low hpd for 500ms for better compatibility */ if (IsHWPortSelected() && (diff_hpd_status & hpd_status & (1 << cur_port))&& (fInRange == FALSE) ) { HDMI_DelayMs(20); bToggle = true; } if (bToggle) { hdmi_apply_hpd((1 << cur_port), DRV_HPD_LEVEL_LOW); HDMI_DelayMs(HDMI_HPD_L2H_DELAY); hdmi_apply_hpd((1 << cur_port), DRV_HPD_LEVEL_HIGH); bToggle = false; } #endif /* Get switch hpd */ local_switch_hpd_status = hdmi_switch_hpd_status() & hpd_mask_sw; diff_switch_hpd_status = local_switch_hpd_status ^ switch_hpd_status; switch_hpd_status = local_switch_hpd_status; /* Patch: For phy issue, Reset hw and phy when current hdmi channel is a hw port and plugged */ if (IsHDMISelected() && (diff_hpd_status & (1 << cur_port)) &&( DrvHDMIPortSelectBitsGet()!=0x3)) { set_reset_phy(); } if (diff_hpd_status || diff_switch_hpd_status) { /* check no signal case when hdmi source is selected and hpd is changed. */ if (IsHDMISelected() && (diff_hpd_status & (1 << cur_port)) &&( DrvHDMIPortSelectBitsGet()!=0x3)) { hdmi_signal_check_start(); } if (IsHDMISelected() && (diff_hpd_status & (1 << cur_port)) &&( DrvHDMIPortSelectBitsGet()!=0x3)) { if( hpd_status & (1 << cur_port))//Current Port HDMI 5V HIGH { HDMI_NoticeHandler(HDMINOTICE_PROCESSING_IN, "Current Port Plug In"); } else { //After plug in S+8203r HDMI Tester Source,Box IBT 1073 NG HDMI_RegisterWrite(HDMIRX_R_sw_hdcp_rstn, 0); HDMI_RegisterWrite(HDMIRX_R_sw_hdcp_rstn, 1); HDMI_NoticeHandler(HDMINOTICE_PROCESSING_OUT, "Current Port Plug Out"); #ifdef HDMI_HPD_USE_1K_OHM if(MHL_CABLE_IN == FALSE) { if( DrvHDMIPortSelectBitsGet()==0x0) { sysset_HDMI_HPD_1K_OnOff(HDMI_PORT_A, TRUE); } else if( DrvHDMIPortSelectBitsGet()==0x1) { sysset_HDMI_HPD_1K_OnOff(HDMI_PORT_B, TRUE); } else if( DrvHDMIPortSelectBitsGet()==0x2) { sysset_HDMI_HPD_1K_OnOff(HDMI_PORT_C, TRUE); } } #endif } } } /* Notice hdmi status */ hdmi_repot_hpd_status(); } void hdmi_hpd_update(void) { #ifdef HDMI_DDC5V_WORKAROUND hpd_debounce(NULL); #else /* Use 100 ms debounce to handle un-stable hpd interrupts */ debounce_notice(&hpd_db, NULL, 100); #endif } HDMI_PORT_T hdmi_get_cur_port(void) { hdmidbg("%s %d\n", __FUNCTION__, cur_port); return cur_port; } void hdmi_hpd_init(void) { INT32 i; init_timer(&term_timer); #ifndef HDMI_DDC5V_WORKAROUND debounce_init(&hpd_db, hpd_debounce); #endif hdmi_status = 0x0; hpd_status = 0x0; switch_hpd_status = 0x0; hpd_mask_hw = 0; hpd_mask_sw = 0; for (i = 0; i < 4; i++) { if (hdmi_get_channel_at_hw_port(i) != HDMI_SRC_NULL) { hpd_mask_hw |= (1 << i); } if (hdmi_get_channel_at_switch_port(i) != HDMI_SRC_NULL) { hpd_mask_sw |= (1 << i); } } //reset hpd value when hdmi connecter with-out signal if((HDMI_RegisterRead(CEC_ddc5v_analog_0) == 0) && (HDMI_RegisterRead(CEC_R_hpd_val_0) == 1)) {HDMI_RegisterWrite(CEC_R_hpd_val_0, 0);hdmidbg("set CEC_R_hpd_val_0 0");} if((HDMI_RegisterRead(CEC_ddc5v_analog_1) == 0) && (HDMI_RegisterRead(CEC_R_hpd_val_1) == 1)) {HDMI_RegisterWrite(CEC_R_hpd_val_1, 0);hdmidbg("set CEC_R_hpd_val_1 0");} if((HDMI_RegisterRead(CEC_ddc5v_analog_2) == 0) && (HDMI_RegisterRead(CEC_R_hpd_val_2) == 1)) {HDMI_RegisterWrite(CEC_R_hpd_val_2, 0);hdmidbg("set CEC_R_hpd_val_2 0");} } void hdmi_hpd_handler(HDMI_SRC_T src) { cur_src = src; cur_port = hdmi_hw_port(src); cur_sw_port = hdmi_sw_port(src); if (IsHDMISelected()) { /* Enter hdmi source */ /* Patch: Toggle hpd low for 500 ms */ if (IsHWPortSelected() && (hpd_status & (1 << cur_port))) { bToggle = true; hdmi_hpd_update(); } } else { /* Leave hdmi source */ bToggle = false; } }